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,33 +1,7 @@
1
- import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useState } from "react";
3
3
  import { Box, Text, useInput } from "ink";
4
- import { BASH_TOOL_NAME, EDIT_TOOL_NAME, MULTI_EDIT_TOOL_NAME, DELETE_FILE_TOOL_NAME, WRITE_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ASK_USER_QUESTION_TOOL_NAME, } from "wave-agent-sdk";
5
- import { DiffDisplay } from "./DiffDisplay.js";
6
- import { PlanDisplay } from "./PlanDisplay.js";
7
- // Helper function to generate descriptive action text
8
- const getActionDescription = (toolName, toolInput) => {
9
- if (!toolInput) {
10
- return "Execute operation";
11
- }
12
- switch (toolName) {
13
- case BASH_TOOL_NAME:
14
- return `Execute command: ${toolInput.command || "unknown command"}`;
15
- case EDIT_TOOL_NAME:
16
- return `Edit file: ${toolInput.file_path || "unknown file"}`;
17
- case MULTI_EDIT_TOOL_NAME:
18
- return `Edit multiple sections in: ${toolInput.file_path || "unknown file"}`;
19
- case DELETE_FILE_TOOL_NAME:
20
- return `Delete file: ${toolInput.target_file || "unknown file"}`;
21
- case WRITE_TOOL_NAME:
22
- return `Write to file: ${toolInput.file_path || "unknown file"}`;
23
- case EXIT_PLAN_MODE_TOOL_NAME:
24
- return "Review and approve the plan";
25
- case ASK_USER_QUESTION_TOOL_NAME:
26
- return "Answer questions to clarify intent";
27
- default:
28
- return "Execute operation";
29
- }
30
- };
4
+ import { BASH_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ASK_USER_QUESTION_TOOL_NAME, } from "wave-agent-sdk";
31
5
  const getHeaderColor = (header) => {
32
6
  const colors = ["red", "green", "blue", "magenta", "cyan"];
33
7
  let hash = 0;
@@ -36,18 +10,19 @@ const getHeaderColor = (header) => {
36
10
  }
37
11
  return colors[Math.abs(hash) % colors.length];
38
12
  };
39
- export const Confirmation = ({ toolName, toolInput, suggestedPrefix, hidePersistentOption, isExpanded = false, onDecision, onCancel, onAbort, }) => {
13
+ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hidePersistentOption, isExpanded = false, onDecision, onCancel, onAbort, }) => {
40
14
  const [state, setState] = useState({
41
15
  selectedOption: "allow",
42
16
  alternativeText: "",
17
+ alternativeCursorPosition: 0,
43
18
  hasUserInput: false,
44
19
  });
45
- // Specialized state for AskUserQuestion
46
20
  const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
47
21
  const [selectedOptionIndex, setSelectedOptionIndex] = useState(0);
48
22
  const [selectedOptionIndices, setSelectedOptionIndices] = useState(new Set());
49
23
  const [userAnswers, setUserAnswers] = useState({});
50
24
  const [otherText, setOtherText] = useState("");
25
+ const [otherCursorPosition, setOtherCursorPosition] = useState(0);
51
26
  const questions = toolInput?.questions || [];
52
27
  const currentQuestion = questions[currentQuestionIndex];
53
28
  const getAutoOptionText = () => {
@@ -63,7 +38,6 @@ export const Confirmation = ({ toolName, toolInput, suggestedPrefix, hidePersist
63
38
  return "Yes, and auto-accept edits";
64
39
  };
65
40
  useInput((input, key) => {
66
- // Handle ESC to cancel and abort
67
41
  if (key.escape) {
68
42
  onCancel();
69
43
  onAbort();
@@ -73,7 +47,7 @@ export const Confirmation = ({ toolName, toolInput, suggestedPrefix, hidePersist
73
47
  if (!currentQuestion)
74
48
  return;
75
49
  const options = [...currentQuestion.options, { label: "Other" }];
76
- const isMultiSelect = !!currentQuestion.multiSelect;
50
+ const isMultiSelect = currentQuestion.multiSelect;
77
51
  const isOtherFocused = selectedOptionIndex === options.length - 1;
78
52
  if (key.return) {
79
53
  let answer = "";
@@ -107,9 +81,9 @@ export const Confirmation = ({ toolName, toolInput, suggestedPrefix, hidePersist
107
81
  setSelectedOptionIndex(0);
108
82
  setSelectedOptionIndices(new Set());
109
83
  setOtherText("");
84
+ setOtherCursorPosition(0);
110
85
  }
111
86
  else {
112
- // All questions answered
113
87
  onDecision({
114
88
  behavior: "allow",
115
89
  message: JSON.stringify(newAnswers),
@@ -122,45 +96,54 @@ export const Confirmation = ({ toolName, toolInput, suggestedPrefix, hidePersist
122
96
  (!isOtherFocused || !selectedOptionIndices.has(selectedOptionIndex))) {
123
97
  setSelectedOptionIndices((prev) => {
124
98
  const next = new Set(prev);
125
- if (next.has(selectedOptionIndex)) {
99
+ if (next.has(selectedOptionIndex))
126
100
  next.delete(selectedOptionIndex);
127
- }
128
- else {
101
+ else
129
102
  next.add(selectedOptionIndex);
130
- }
131
103
  return next;
132
104
  });
133
105
  return;
134
106
  }
135
- if (!isOtherFocused) {
107
+ if (!isOtherFocused)
136
108
  return;
137
- }
138
- // If isOtherFocused is true, fall through to handle space as text input
139
109
  }
140
110
  if (key.upArrow) {
141
- if (selectedOptionIndex > 0) {
111
+ if (selectedOptionIndex > 0)
142
112
  setSelectedOptionIndex(selectedOptionIndex - 1);
143
- }
144
113
  return;
145
114
  }
146
115
  if (key.downArrow) {
147
- if (selectedOptionIndex < options.length - 1) {
116
+ if (selectedOptionIndex < options.length - 1)
148
117
  setSelectedOptionIndex(selectedOptionIndex + 1);
149
- }
150
118
  return;
151
119
  }
152
120
  if (isOtherFocused) {
121
+ if (key.leftArrow) {
122
+ setOtherCursorPosition((prev) => Math.max(0, prev - 1));
123
+ return;
124
+ }
125
+ if (key.rightArrow) {
126
+ setOtherCursorPosition((prev) => Math.min(otherText.length, prev + 1));
127
+ return;
128
+ }
153
129
  if (key.backspace || key.delete) {
154
- setOtherText((prev) => prev.slice(0, -1));
130
+ if (otherCursorPosition > 0) {
131
+ setOtherText((prev) => prev.slice(0, otherCursorPosition - 1) +
132
+ prev.slice(otherCursorPosition));
133
+ setOtherCursorPosition((prev) => prev - 1);
134
+ }
135
+ return;
155
136
  }
156
- else if (input && !key.ctrl && !key.meta) {
157
- setOtherText((prev) => prev + input);
137
+ if (input && !key.ctrl && !key.meta) {
138
+ setOtherText((prev) => prev.slice(0, otherCursorPosition) +
139
+ input +
140
+ prev.slice(otherCursorPosition));
141
+ setOtherCursorPosition((prev) => prev + input.length);
142
+ return;
158
143
  }
159
- return;
160
144
  }
161
145
  return;
162
146
  }
163
- // Handle Enter to confirm selection
164
147
  if (key.return) {
165
148
  if (state.selectedOption === "allow") {
166
149
  if (toolName === EXIT_PLAN_MODE_TOOL_NAME) {
@@ -175,38 +158,40 @@ export const Confirmation = ({ toolName, toolInput, suggestedPrefix, hidePersist
175
158
  const rule = suggestedPrefix
176
159
  ? `Bash(${suggestedPrefix}*)`
177
160
  : `Bash(${toolInput?.command})`;
178
- onDecision({
179
- behavior: "allow",
180
- newPermissionRule: rule,
181
- });
161
+ onDecision({ behavior: "allow", newPermissionRule: rule });
182
162
  }
183
163
  else {
184
- onDecision({
185
- behavior: "allow",
186
- newPermissionMode: "acceptEdits",
187
- });
164
+ onDecision({ behavior: "allow", newPermissionMode: "acceptEdits" });
188
165
  }
189
166
  }
190
- else {
191
- // For alternative option, require text input
192
- if (state.alternativeText.trim()) {
193
- onDecision({
194
- behavior: "deny",
195
- message: state.alternativeText.trim(),
196
- });
197
- }
167
+ else if (state.alternativeText.trim()) {
168
+ onDecision({ behavior: "deny", message: state.alternativeText.trim() });
198
169
  }
199
170
  return;
200
171
  }
201
- // Handle arrow keys for navigation
172
+ if (state.selectedOption === "alternative") {
173
+ if (key.leftArrow) {
174
+ setState((prev) => ({
175
+ ...prev,
176
+ alternativeCursorPosition: Math.max(0, prev.alternativeCursorPosition - 1),
177
+ }));
178
+ return;
179
+ }
180
+ if (key.rightArrow) {
181
+ setState((prev) => ({
182
+ ...prev,
183
+ alternativeCursorPosition: Math.min(prev.alternativeText.length, prev.alternativeCursorPosition + 1),
184
+ }));
185
+ return;
186
+ }
187
+ }
202
188
  if (key.upArrow) {
203
189
  setState((prev) => {
204
- if (prev.selectedOption === "alternative") {
190
+ if (prev.selectedOption === "alternative")
205
191
  return {
206
192
  ...prev,
207
193
  selectedOption: hidePersistentOption ? "allow" : "auto",
208
194
  };
209
- }
210
195
  if (prev.selectedOption === "auto")
211
196
  return { ...prev, selectedOption: "allow" };
212
197
  return prev;
@@ -215,61 +200,67 @@ export const Confirmation = ({ toolName, toolInput, suggestedPrefix, hidePersist
215
200
  }
216
201
  if (key.downArrow) {
217
202
  setState((prev) => {
218
- if (prev.selectedOption === "allow") {
203
+ if (prev.selectedOption === "allow")
219
204
  return {
220
205
  ...prev,
221
206
  selectedOption: hidePersistentOption ? "alternative" : "auto",
222
207
  };
223
- }
224
208
  if (prev.selectedOption === "auto")
225
209
  return { ...prev, selectedOption: "alternative" };
226
210
  return prev;
227
211
  });
228
212
  return;
229
213
  }
230
- // Handle text input for alternative option
231
214
  if (input && !key.ctrl && !key.meta && !("alt" in key && key.alt)) {
232
- // Focus on alternative option when user starts typing
233
- setState((prev) => ({
234
- selectedOption: "alternative",
235
- alternativeText: prev.alternativeText + input,
236
- hasUserInput: true,
237
- }));
238
- return;
239
- }
240
- // Handle backspace and delete (same behavior - delete one character)
241
- if (key.backspace || key.delete) {
242
215
  setState((prev) => {
243
- const newText = prev.alternativeText.slice(0, -1);
216
+ const nextText = prev.alternativeText.slice(0, prev.alternativeCursorPosition) +
217
+ input +
218
+ prev.alternativeText.slice(prev.alternativeCursorPosition);
244
219
  return {
245
220
  ...prev,
246
221
  selectedOption: "alternative",
247
- alternativeText: newText,
248
- hasUserInput: newText.length > 0,
222
+ alternativeText: nextText,
223
+ alternativeCursorPosition: prev.alternativeCursorPosition + input.length,
224
+ hasUserInput: true,
249
225
  };
250
226
  });
251
227
  return;
252
228
  }
229
+ if (key.backspace || key.delete) {
230
+ setState((prev) => {
231
+ if (prev.alternativeCursorPosition > 0) {
232
+ const nextText = prev.alternativeText.slice(0, prev.alternativeCursorPosition - 1) +
233
+ prev.alternativeText.slice(prev.alternativeCursorPosition);
234
+ return {
235
+ ...prev,
236
+ selectedOption: "alternative",
237
+ alternativeText: nextText,
238
+ alternativeCursorPosition: prev.alternativeCursorPosition - 1,
239
+ hasUserInput: nextText.length > 0,
240
+ };
241
+ }
242
+ return prev;
243
+ });
244
+ return;
245
+ }
253
246
  });
254
247
  const placeholderText = "Type here to tell Wave what to do differently";
255
248
  const showPlaceholder = state.selectedOption === "alternative" && !state.hasUserInput;
256
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "yellow", borderBottom: false, borderLeft: false, borderRight: false, paddingTop: 1, children: [_jsxs(Text, { color: "yellow", bold: true, children: ["Tool: ", toolName] }), _jsx(Text, { color: "yellow", children: getActionDescription(toolName, toolInput) }), _jsx(DiffDisplay, { toolName: toolName, parameters: JSON.stringify(toolInput), isExpanded: isExpanded }), toolName === ASK_USER_QUESTION_TOOL_NAME &&
249
+ return (_jsxs(Box, { flexDirection: "column", children: [toolName === ASK_USER_QUESTION_TOOL_NAME &&
257
250
  currentQuestion &&
258
- !isExpanded && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Box, { backgroundColor: getHeaderColor(currentQuestion.header), paddingX: 1, marginRight: 1, children: _jsx(Text, { color: "black", bold: true, children: currentQuestion.header.slice(0, 12).toUpperCase() }) }), _jsx(Text, { bold: true, children: currentQuestion.question })] }), _jsx(Box, { flexDirection: "column", children: (() => {
259
- const isMultiSelect = !!currentQuestion.multiSelect;
260
- return [...currentQuestion.options, { label: "Other" }].map((option, index) => {
261
- const isSelected = selectedOptionIndex === index;
262
- const isChecked = isMultiSelect
263
- ? selectedOptionIndices.has(index)
264
- : isSelected;
265
- const isOther = index === currentQuestion.options.length;
266
- const isRecommended = !isOther && option.isRecommended;
267
- return (_jsx(Box, { children: _jsxs(Text, { color: isSelected ? "black" : "white", backgroundColor: isSelected ? "yellow" : undefined, children: [isSelected ? "> " : " ", isMultiSelect ? (isChecked ? "[x] " : "[ ] ") : "", option.label, isRecommended && (_jsxs(Text, { color: "green", bold: true, children: [" ", "(Recommended)"] })), option.description ? ` - ${option.description}` : "", isOther && isSelected && (_jsxs(Text, { children: [":", " ", otherText || (_jsx(Text, { color: "gray", dimColor: true, children: "[Type your answer...]" }))] }))] }) }, index));
268
- });
269
- })() }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["Question ", currentQuestionIndex + 1, " of ", questions.length, " \u2022", currentQuestion.multiSelect ? " Space to toggle •" : "", " Use \u2191\u2193 to navigate \u2022 Enter to confirm"] }) })] })), toolName !== ASK_USER_QUESTION_TOOL_NAME &&
270
- toolName === EXIT_PLAN_MODE_TOOL_NAME &&
271
- !!toolInput?.plan_content && (_jsx(PlanDisplay, { plan: toolInput.plan_content, isExpanded: isExpanded })), toolName !== ASK_USER_QUESTION_TOOL_NAME && !isExpanded && (_jsxs(_Fragment, { children: [_jsx(Box, { marginTop: 1, children: _jsx(Text, { children: "Do you want to proceed?" }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "allow" ? "black" : "white", backgroundColor: state.selectedOption === "allow" ? "yellow" : undefined, bold: state.selectedOption === "allow", children: [state.selectedOption === "allow" ? "> " : " ", toolName === EXIT_PLAN_MODE_TOOL_NAME
251
+ !isExpanded && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Box, { backgroundColor: getHeaderColor(currentQuestion.header), paddingX: 1, marginRight: 1, children: _jsx(Text, { color: "black", bold: true, children: currentQuestion.header.slice(0, 12).toUpperCase() }) }), _jsx(Text, { bold: true, children: currentQuestion.question })] }), _jsx(Box, { flexDirection: "column", children: [...currentQuestion.options, { label: "Other" }].map((option, index) => {
252
+ const isSelected = selectedOptionIndex === index;
253
+ const isChecked = currentQuestion.multiSelect
254
+ ? selectedOptionIndices.has(index)
255
+ : isSelected;
256
+ const isOther = index === currentQuestion.options.length;
257
+ return (_jsx(Box, { children: _jsxs(Text, { color: isSelected ? "black" : "white", backgroundColor: isSelected ? "yellow" : undefined, children: [isSelected ? "> " : " ", currentQuestion.multiSelect
258
+ ? isChecked
259
+ ? "[x] "
260
+ : "[ ] "
261
+ : "", option.label, option.description ? ` - ${option.description}` : "", isOther && isSelected && (_jsxs(Text, { children: [":", " ", otherText ? (_jsxs(_Fragment, { children: [otherText.slice(0, otherCursorPosition), _jsx(Text, { backgroundColor: "white", color: "black", children: otherText[otherCursorPosition] || " " }), otherText.slice(otherCursorPosition + 1)] })) : (_jsx(Text, { color: "gray", dimColor: true, children: "[Type your answer...]" }))] }))] }) }, index));
262
+ }) }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["Question ", currentQuestionIndex + 1, " of ", questions.length, " \u2022", currentQuestion.multiSelect ? " Space to toggle •" : "", " Use \u2191\u2193 to navigate \u2022 Enter to confirm"] }) })] })), toolName !== ASK_USER_QUESTION_TOOL_NAME && !isExpanded && (_jsxs(_Fragment, { children: [_jsx(Box, { marginTop: 1, children: _jsx(Text, { children: "Do you want to proceed?" }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "allow" ? "black" : "white", backgroundColor: state.selectedOption === "allow" ? "yellow" : undefined, bold: state.selectedOption === "allow", children: [state.selectedOption === "allow" ? "> " : " ", toolName === EXIT_PLAN_MODE_TOOL_NAME
272
263
  ? "Yes, proceed with default mode"
273
- : "Yes"] }) }, "allow-option"), !hidePersistentOption && (_jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "auto" ? "black" : "white", backgroundColor: state.selectedOption === "auto" ? "yellow" : undefined, bold: state.selectedOption === "auto", children: [state.selectedOption === "auto" ? "> " : " ", getAutoOptionText()] }) }, "auto-option")), _jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "alternative" ? "black" : "white", backgroundColor: state.selectedOption === "alternative" ? "yellow" : undefined, bold: state.selectedOption === "alternative", children: [state.selectedOption === "alternative" ? "> " : " ", showPlaceholder ? (_jsx(Text, { color: "gray", dimColor: true, children: placeholderText })) : (_jsx(Text, { children: state.alternativeText ||
274
- "Type here to tell Wave what to do differently" }))] }) }, "alternative-option")] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Use \u2191\u2193 to navigate \u2022 ESC to cancel" }) })] }))] }));
264
+ : "Yes"] }) }, "allow-option"), !hidePersistentOption && (_jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "auto" ? "black" : "white", backgroundColor: state.selectedOption === "auto" ? "yellow" : undefined, bold: state.selectedOption === "auto", children: [state.selectedOption === "auto" ? "> " : " ", getAutoOptionText()] }) }, "auto-option")), _jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "alternative" ? "black" : "white", backgroundColor: state.selectedOption === "alternative" ? "yellow" : undefined, bold: state.selectedOption === "alternative", children: [state.selectedOption === "alternative" ? "> " : " ", showPlaceholder ? (_jsx(Text, { color: "gray", dimColor: true, children: placeholderText })) : (_jsx(Text, { children: state.alternativeText ? (_jsxs(_Fragment, { children: [state.alternativeText.slice(0, state.alternativeCursorPosition), _jsx(Text, { backgroundColor: "white", color: "black", children: state.alternativeText[state.alternativeCursorPosition] || " " }), state.alternativeText.slice(state.alternativeCursorPosition + 1)] })) : ("Type here to tell Wave what to do differently") }))] }) }, "alternative-option")] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Use \u2191\u2193 to navigate \u2022 ESC to cancel" }) })] }))] }));
275
265
  };
266
+ ConfirmationSelector.displayName = "ConfirmationSelector";
@@ -2,7 +2,6 @@ import React from "react";
2
2
  interface DiffDisplayProps {
3
3
  toolName?: string;
4
4
  parameters?: string;
5
- isExpanded?: boolean;
6
5
  }
7
6
  export declare const DiffDisplay: React.FC<DiffDisplayProps>;
8
7
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"DiffDisplay.d.ts","sourceRoot":"","sources":["../../src/components/DiffDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAUvC,UAAU,gBAAgB;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CA6SlD,CAAC"}
1
+ {"version":3,"file":"DiffDisplay.d.ts","sourceRoot":"","sources":["../../src/components/DiffDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAUvC,UAAU,gBAAgB;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAySlD,CAAC"}
@@ -1,14 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React, { useMemo } from "react";
3
- import { Box, Text, useStdout } from "ink";
3
+ import { Box, Text } from "ink";
4
4
  import { WRITE_TOOL_NAME, EDIT_TOOL_NAME, MULTI_EDIT_TOOL_NAME, } from "wave-agent-sdk";
5
5
  import { transformToolBlockToChanges } from "../utils/toolParameterTransforms.js";
6
6
  import { diffLines, diffWords } from "diff";
7
- export const DiffDisplay = ({ toolName, parameters, isExpanded = false, }) => {
8
- const { stdout } = useStdout();
9
- const maxHeight = useMemo(() => {
10
- return Math.max(5, (stdout?.rows || 24) - 20);
11
- }, [stdout?.rows]);
7
+ export const DiffDisplay = ({ toolName, parameters, }) => {
12
8
  const showDiff = toolName &&
13
9
  [WRITE_TOOL_NAME, EDIT_TOOL_NAME, MULTI_EDIT_TOOL_NAME].includes(toolName);
14
10
  // Diff detection and transformation using typed parameters
@@ -58,7 +54,7 @@ export const DiffDisplay = ({ toolName, parameters, isExpanded = false, }) => {
58
54
  };
59
55
  }
60
56
  };
61
- // Render expanded diff display using word-level diff for all changes
57
+ // Render expanded diff display
62
58
  const renderExpandedDiff = () => {
63
59
  try {
64
60
  if (changes.length === 0)
@@ -68,8 +64,8 @@ export const DiffDisplay = ({ toolName, parameters, isExpanded = false, }) => {
68
64
  try {
69
65
  // Get line-level diff to understand the structure
70
66
  const lineDiffs = diffLines(change.oldContent || "", change.newContent || "");
67
+ // Process line diffs
71
68
  const diffElements = [];
72
- // Process line diffs and apply word-level diff to changed lines
73
69
  lineDiffs.forEach((part, partIndex) => {
74
70
  if (part.added) {
75
71
  const lines = part.value
@@ -92,46 +88,76 @@ export const DiffDisplay = ({ toolName, parameters, isExpanded = false, }) => {
92
88
  const lines = part.value
93
89
  .split("\n")
94
90
  .filter((line) => line !== "");
95
- lines.forEach((line, lineIndex) => {
91
+ const isFirstBlock = partIndex === 0;
92
+ const isLastBlock = partIndex === lineDiffs.length - 1;
93
+ let linesToDisplay = lines;
94
+ let showEllipsisTop = false;
95
+ let showEllipsisBottom = false;
96
+ if (isFirstBlock && !isLastBlock) {
97
+ // First block: keep last 3
98
+ if (lines.length > 3) {
99
+ linesToDisplay = lines.slice(-3);
100
+ showEllipsisTop = true;
101
+ }
102
+ }
103
+ else if (isLastBlock && !isFirstBlock) {
104
+ // Last block: keep first 3
105
+ if (lines.length > 3) {
106
+ linesToDisplay = lines.slice(0, 3);
107
+ showEllipsisBottom = true;
108
+ }
109
+ }
110
+ else if (!isFirstBlock && !isLastBlock) {
111
+ // Middle block: keep first 3 and last 3
112
+ if (lines.length > 6) {
113
+ linesToDisplay = [...lines.slice(0, 3), ...lines.slice(-3)];
114
+ showEllipsisTop = false; // We'll put ellipsis in the middle
115
+ }
116
+ }
117
+ else if (isFirstBlock && isLastBlock) {
118
+ // Only one block (no changes?) - keep all or apply a general limit
119
+ // For now, let's keep all if it's the only block
120
+ }
121
+ if (showEllipsisTop) {
122
+ diffElements.push(_jsx(Box, { children: _jsx(Text, { color: "gray", children: " ..." }) }, `ellipsis-top-${changeIndex}-${partIndex}`));
123
+ }
124
+ linesToDisplay.forEach((line, lineIndex) => {
125
+ // If it's a middle block and we are at the split point
126
+ if (!isFirstBlock &&
127
+ !isLastBlock &&
128
+ lines.length > 6 &&
129
+ lineIndex === 3) {
130
+ diffElements.push(_jsx(Box, { children: _jsx(Text, { color: "gray", children: " ..." }) }, `ellipsis-mid-${changeIndex}-${partIndex}`));
131
+ }
96
132
  diffElements.push(_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "white", children: " " }), _jsx(Text, { color: "white", children: line })] }, `context-${changeIndex}-${partIndex}-${lineIndex}`));
97
133
  });
134
+ if (showEllipsisBottom) {
135
+ diffElements.push(_jsx(Box, { children: _jsx(Text, { color: "gray", children: " ..." }) }, `ellipsis-bottom-${changeIndex}-${partIndex}`));
136
+ }
98
137
  }
99
138
  });
100
- // Now look for pairs of removed/added lines that can be word-diffed
101
- let i = 0;
102
- while (i < diffElements.length) {
103
- const current = diffElements[i];
104
- const next = i + 1 < diffElements.length ? diffElements[i + 1] : null;
105
- // Check if we have a removed line followed by an added line
106
- const currentKey = React.isValidElement(current) ? current.key : "";
107
- const nextKey = React.isValidElement(next) ? next.key : "";
108
- const isCurrentRemoved = typeof currentKey === "string" && currentKey.includes("remove-");
109
- const isNextAdded = typeof nextKey === "string" && nextKey.includes("add-");
110
- if (isCurrentRemoved &&
111
- isNextAdded &&
112
- React.isValidElement(current) &&
113
- React.isValidElement(next)) {
114
- // Extract the text content from the removed and added lines
115
- const removedText = extractTextFromElement(current);
116
- const addedText = extractTextFromElement(next);
117
- if (removedText && addedText) {
118
- // Apply word-level diff
119
- const { removedParts, addedParts } = renderWordLevelDiff(removedText, addedText, `word-${changeIndex}-${i}`);
120
- allElements.push(_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "red", children: "-" }), removedParts] }, `word-diff-removed-${changeIndex}-${i}`));
121
- allElements.push(_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "green", children: "+" }), addedParts] }, `word-diff-added-${changeIndex}-${i}`));
122
- i += 2; // Skip the next element since we processed it
123
- }
124
- else {
125
- // Fallback to original elements
126
- allElements.push(current);
127
- i += 1;
128
- }
139
+ // If it's a single line change (one removed, one added), use word-level diff
140
+ if (diffElements.length === 2 &&
141
+ React.isValidElement(diffElements[0]) &&
142
+ React.isValidElement(diffElements[1]) &&
143
+ typeof diffElements[0].key === "string" &&
144
+ diffElements[0].key.includes("remove-") &&
145
+ typeof diffElements[1].key === "string" &&
146
+ diffElements[1].key.includes("add-")) {
147
+ const removedText = extractTextFromElement(diffElements[0]);
148
+ const addedText = extractTextFromElement(diffElements[1]);
149
+ if (removedText && addedText) {
150
+ const { removedParts, addedParts } = renderWordLevelDiff(removedText, addedText, `word-${changeIndex}`);
151
+ allElements.push(_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "red", children: "-" }), removedParts] }, `word-diff-removed-${changeIndex}`));
152
+ allElements.push(_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "green", children: "+" }), addedParts] }, `word-diff-added-${changeIndex}`));
129
153
  }
130
154
  else {
131
- allElements.push(current);
132
- i += 1;
155
+ allElements.push(...diffElements);
133
156
  }
134
157
  }
158
+ else {
159
+ allElements.push(...diffElements);
160
+ }
135
161
  }
136
162
  catch (error) {
137
163
  console.warn(`Error rendering diff for change ${changeIndex}:`, error);
@@ -139,35 +165,31 @@ export const DiffDisplay = ({ toolName, parameters, isExpanded = false, }) => {
139
165
  allElements.push(_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "red", children: ["-", change.oldContent || ""] }), _jsxs(Text, { color: "green", children: ["+", change.newContent || ""] })] }, `fallback-${changeIndex}`));
140
166
  }
141
167
  });
142
- const isTruncated = !isExpanded && allElements.length > maxHeight;
143
- const displayElements = isTruncated
144
- ? allElements.slice(0, maxHeight - 1)
145
- : allElements;
146
- return (_jsxs(Box, { flexDirection: "column", children: [displayElements, isTruncated && (_jsxs(Text, { color: "yellow", dimColor: true, children: ["... (truncated ", allElements.length - (maxHeight - 1), " more lines)"] }))] }));
168
+ return _jsx(Box, { flexDirection: "column", children: allElements });
147
169
  }
148
170
  catch (error) {
149
171
  console.warn("Error rendering expanded diff:", error);
150
172
  return (_jsx(Box, { children: _jsx(Text, { color: "gray", children: "Error rendering diff display" }) }));
151
173
  }
152
174
  };
153
- // Helper function to extract text content from a React element
154
- const extractTextFromElement = (element) => {
155
- if (!React.isValidElement(element))
156
- return null;
157
- // Navigate through Box -> Text structure
158
- const children = element.props.children;
159
- if (Array.isArray(children) && children.length >= 2) {
160
- const textElement = children[1]; // Second child should be the Text with content
161
- if (React.isValidElement(textElement) &&
162
- textElement.props.children) {
163
- return textElement.props.children;
164
- }
165
- }
166
- return null;
167
- };
168
175
  // Don't render anything if no diff should be shown
169
176
  if (!showDiff) {
170
177
  return null;
171
178
  }
172
179
  return (_jsx(Box, { flexDirection: "column", children: _jsxs(Box, { paddingLeft: 2, borderLeft: true, borderColor: "cyan", flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Diff:" }), renderExpandedDiff()] }) }));
173
180
  };
181
+ // Helper function to extract text content from a React element
182
+ const extractTextFromElement = (element) => {
183
+ if (!React.isValidElement(element))
184
+ return null;
185
+ // Navigate through Box -> Text structure
186
+ const children = element.props.children;
187
+ if (Array.isArray(children) && children.length >= 2) {
188
+ const textElement = children[1]; // Second child should be the Text with content
189
+ if (React.isValidElement(textElement) &&
190
+ textElement.props.children) {
191
+ return textElement.props.children;
192
+ }
193
+ }
194
+ return null;
195
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"FileSelector.d.ts","sourceRoot":"","sources":["../../src/components/FileSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA6HpD,CAAC"}
1
+ {"version":3,"file":"FileSelector.d.ts","sourceRoot":"","sources":["../../src/components/FileSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA2HpD,CAAC"}
@@ -24,7 +24,7 @@ export const FileSelector = ({ files, searchQuery, onSelect, onCancel, }) => {
24
24
  }
25
25
  });
26
26
  if (files.length === 0) {
27
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "yellow", borderBottom: false, borderLeft: false, borderRight: false, paddingTop: 1, children: [_jsxs(Text, { color: "yellow", children: ["\uD83D\uDCC1 No files found for \"", searchQuery, "\""] }), _jsx(Text, { dimColor: true, children: "Press Escape to cancel" })] }));
27
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "yellow", borderBottom: false, borderLeft: false, borderRight: false, children: [_jsxs(Text, { color: "yellow", children: ["\uD83D\uDCC1 No files found for \"", searchQuery, "\""] }), _jsx(Text, { dimColor: true, children: "Press Escape to cancel" })] }));
28
28
  }
29
29
  const maxDisplay = 10;
30
30
  // Calculate display window start and end positions
@@ -39,7 +39,7 @@ export const FileSelector = ({ files, searchQuery, onSelect, onCancel, }) => {
39
39
  };
40
40
  };
41
41
  const { startIndex, endIndex, displayFiles } = getDisplayWindow();
42
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", borderBottom: false, borderLeft: false, borderRight: false, paddingTop: 1, children: [_jsxs(Text, { color: "cyan", bold: true, children: ["\uD83D\uDCC1 Select File/Directory", " ", searchQuery && `(filtering: "${searchQuery}")`] }), startIndex > 0 && (_jsxs(Text, { dimColor: true, children: ["... ", startIndex, " more files above"] })), displayFiles.map((fileItem, displayIndex) => {
42
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", borderBottom: false, borderLeft: false, borderRight: false, children: [_jsxs(Text, { color: "cyan", bold: true, children: ["\uD83D\uDCC1 Select File/Directory", " ", searchQuery && `(filtering: "${searchQuery}")`] }), startIndex > 0 && (_jsxs(Text, { dimColor: true, children: ["... ", startIndex, " more files above"] })), displayFiles.map((fileItem, displayIndex) => {
43
43
  const actualIndex = startIndex + displayIndex;
44
44
  const isSelected = actualIndex === selectedIndex;
45
45
  const icon = fileItem.type === "directory" ? "📁" : "📄";
@@ -1 +1 @@
1
- {"version":3,"file":"HistorySearch.d.ts","sourceRoot":"","sources":["../../src/components/HistorySearch.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAyItD,CAAC"}
1
+ {"version":3,"file":"HistorySearch.d.ts","sourceRoot":"","sources":["../../src/components/HistorySearch.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAiKtD,CAAC"}
@@ -3,6 +3,7 @@ import React, { useState, useEffect } from "react";
3
3
  import { Box, Text, useInput } from "ink";
4
4
  import { PromptHistoryManager } from "wave-agent-sdk";
5
5
  export const HistorySearch = ({ searchQuery, onSelect, onCancel, }) => {
6
+ const MAX_VISIBLE_ITEMS = 5;
6
7
  const [selectedIndex, setSelectedIndex] = useState(0);
7
8
  const [entries, setEntries] = useState([]);
8
9
  const entriesRef = React.useRef([]);
@@ -16,8 +17,8 @@ export const HistorySearch = ({ searchQuery, onSelect, onCancel, }) => {
16
17
  useEffect(() => {
17
18
  const fetchHistory = async () => {
18
19
  const results = await PromptHistoryManager.searchHistory(searchQuery);
19
- const limitedResults = results.slice(0, 10);
20
- setEntries(limitedResults); // Limit to 10 results
20
+ const limitedResults = results.slice(0, 20);
21
+ setEntries(limitedResults); // Limit to 20 results
21
22
  setSelectedIndex(0);
22
23
  };
23
24
  fetchHistory();
@@ -44,7 +45,7 @@ export const HistorySearch = ({ searchQuery, onSelect, onCancel, }) => {
44
45
  }
45
46
  });
46
47
  if (entries.length === 0) {
47
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "yellow", borderBottom: false, borderLeft: false, borderRight: false, paddingTop: 1, children: [_jsxs(Text, { color: "yellow", children: ["No history found ", searchQuery && `for "${searchQuery}"`] }), _jsx(Text, { dimColor: true, children: "Press Escape to cancel" })] }));
48
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "yellow", borderBottom: false, borderLeft: false, borderRight: false, children: [_jsxs(Text, { color: "yellow", children: ["No history found ", searchQuery && `for "${searchQuery}"`] }), _jsx(Text, { dimColor: true, children: "Press Escape to cancel" })] }));
48
49
  }
49
50
  const formatTimestamp = (timestamp) => {
50
51
  const date = new Date(timestamp);
@@ -63,5 +64,12 @@ export const HistorySearch = ({ searchQuery, onSelect, onCancel, }) => {
63
64
  return diffMinutes > 0 ? `${diffMinutes}m ago` : "just now";
64
65
  }
65
66
  };
66
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "blue", borderBottom: false, borderLeft: false, borderRight: false, paddingTop: 1, gap: 1, children: [_jsx(Box, { children: _jsxs(Text, { color: "blue", bold: true, children: ["Prompt History ", searchQuery && `(filtering: "${searchQuery}")`] }) }), entries.map((entry, index) => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: index === selectedIndex ? "black" : "white", backgroundColor: index === selectedIndex ? "blue" : undefined, wrap: "truncate-end", children: entry.prompt.replace(/\n/g, " ") }), index === selectedIndex && (_jsx(Box, { marginLeft: 4, children: _jsx(Text, { color: "gray", dimColor: true, children: formatTimestamp(entry.timestamp) }) }))] }, index))), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: "Use \u2191\u2193 to navigate, Enter to select, Escape to cancel" }) })] }));
67
+ // Calculate visible window
68
+ const startIndex = Math.max(0, Math.min(selectedIndex - Math.floor(MAX_VISIBLE_ITEMS / 2), Math.max(0, entries.length - MAX_VISIBLE_ITEMS)));
69
+ const visibleEntries = entries.slice(startIndex, startIndex + MAX_VISIBLE_ITEMS);
70
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "blue", borderBottom: false, borderLeft: false, borderRight: false, gap: 1, children: [_jsx(Box, { children: _jsxs(Text, { color: "blue", bold: true, children: ["Prompt History ", searchQuery && `(filtering: "${searchQuery}")`] }) }), _jsx(Box, { flexDirection: "column", children: visibleEntries.map((entry, index) => {
71
+ const actualIndex = startIndex + index;
72
+ const isSelected = actualIndex === selectedIndex;
73
+ return (_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [_jsx(Box, { flexShrink: 1, children: _jsx(Text, { color: isSelected ? "black" : "white", backgroundColor: isSelected ? "blue" : undefined, wrap: "truncate-end", children: entry.prompt.replace(/\n/g, " ") }) }), isSelected && (_jsx(Box, { marginLeft: 2, flexShrink: 0, children: _jsx(Text, { color: "gray", dimColor: true, children: formatTimestamp(entry.timestamp) }) }))] }, actualIndex));
74
+ }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: "Use \u2191\u2193 to navigate, Enter to select, Escape to cancel" }) })] }));
67
75
  };