wave-code 0.8.1 → 0.8.2
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.
- package/dist/components/ChatInterface.js +1 -1
- package/dist/components/MessageList.d.ts +2 -2
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +5 -6
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +2 -17
- package/dist/hooks/useInputManager.d.ts +7 -8
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +224 -232
- package/dist/managers/inputHandlers.d.ts +28 -0
- package/dist/managers/inputHandlers.d.ts.map +1 -0
- package/dist/managers/inputHandlers.js +378 -0
- package/dist/managers/inputReducer.d.ts +157 -0
- package/dist/managers/inputReducer.d.ts.map +1 -0
- package/dist/managers/inputReducer.js +242 -0
- package/package.json +2 -2
- package/src/components/ChatInterface.tsx +1 -1
- package/src/components/MessageList.tsx +5 -6
- package/src/contexts/useChat.tsx +2 -17
- package/src/hooks/useInputManager.ts +352 -299
- package/src/managers/inputHandlers.ts +560 -0
- package/src/managers/inputReducer.ts +367 -0
- package/dist/managers/InputManager.d.ts +0 -156
- package/dist/managers/InputManager.d.ts.map +0 -1
- package/dist/managers/InputManager.js +0 -749
- package/src/managers/InputManager.ts +0 -1024
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
export const initialState = {
|
|
2
|
+
inputText: "",
|
|
3
|
+
cursorPosition: 0,
|
|
4
|
+
showFileSelector: false,
|
|
5
|
+
atPosition: -1,
|
|
6
|
+
fileSearchQuery: "",
|
|
7
|
+
filteredFiles: [],
|
|
8
|
+
showCommandSelector: false,
|
|
9
|
+
slashPosition: -1,
|
|
10
|
+
commandSearchQuery: "",
|
|
11
|
+
showHistorySearch: false,
|
|
12
|
+
historySearchQuery: "",
|
|
13
|
+
longTextCounter: 0,
|
|
14
|
+
longTextMap: {},
|
|
15
|
+
attachedImages: [],
|
|
16
|
+
imageIdCounter: 1,
|
|
17
|
+
showBackgroundTaskManager: false,
|
|
18
|
+
showMcpManager: false,
|
|
19
|
+
showRewindManager: false,
|
|
20
|
+
showHelp: false,
|
|
21
|
+
showStatusCommand: false,
|
|
22
|
+
permissionMode: "default",
|
|
23
|
+
selectorJustUsed: false,
|
|
24
|
+
isPasting: false,
|
|
25
|
+
pasteBuffer: "",
|
|
26
|
+
initialPasteCursorPosition: 0,
|
|
27
|
+
};
|
|
28
|
+
export function inputReducer(state, action) {
|
|
29
|
+
switch (action.type) {
|
|
30
|
+
case "SET_INPUT_TEXT":
|
|
31
|
+
return { ...state, inputText: action.payload };
|
|
32
|
+
case "SET_CURSOR_POSITION":
|
|
33
|
+
return {
|
|
34
|
+
...state,
|
|
35
|
+
cursorPosition: Math.max(0, Math.min(state.inputText.length, action.payload)),
|
|
36
|
+
};
|
|
37
|
+
case "INSERT_TEXT": {
|
|
38
|
+
const beforeCursor = state.inputText.substring(0, state.cursorPosition);
|
|
39
|
+
const afterCursor = state.inputText.substring(state.cursorPosition);
|
|
40
|
+
const newText = beforeCursor + action.payload + afterCursor;
|
|
41
|
+
const newCursorPosition = state.cursorPosition + action.payload.length;
|
|
42
|
+
return {
|
|
43
|
+
...state,
|
|
44
|
+
inputText: newText,
|
|
45
|
+
cursorPosition: newCursorPosition,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
case "DELETE_CHAR": {
|
|
49
|
+
if (state.cursorPosition > 0) {
|
|
50
|
+
const beforeCursor = state.inputText.substring(0, state.cursorPosition - 1);
|
|
51
|
+
const afterCursor = state.inputText.substring(state.cursorPosition);
|
|
52
|
+
const newText = beforeCursor + afterCursor;
|
|
53
|
+
const newCursorPosition = state.cursorPosition - 1;
|
|
54
|
+
return {
|
|
55
|
+
...state,
|
|
56
|
+
inputText: newText,
|
|
57
|
+
cursorPosition: newCursorPosition,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return state;
|
|
61
|
+
}
|
|
62
|
+
case "MOVE_CURSOR": {
|
|
63
|
+
const newCursorPosition = Math.max(0, Math.min(state.inputText.length, state.cursorPosition + action.payload));
|
|
64
|
+
return { ...state, cursorPosition: newCursorPosition };
|
|
65
|
+
}
|
|
66
|
+
case "ACTIVATE_FILE_SELECTOR":
|
|
67
|
+
return {
|
|
68
|
+
...state,
|
|
69
|
+
showFileSelector: true,
|
|
70
|
+
atPosition: action.payload,
|
|
71
|
+
fileSearchQuery: "",
|
|
72
|
+
filteredFiles: [],
|
|
73
|
+
};
|
|
74
|
+
case "SET_FILE_SEARCH_QUERY":
|
|
75
|
+
return { ...state, fileSearchQuery: action.payload };
|
|
76
|
+
case "SET_FILTERED_FILES":
|
|
77
|
+
return { ...state, filteredFiles: action.payload };
|
|
78
|
+
case "CANCEL_FILE_SELECTOR":
|
|
79
|
+
return {
|
|
80
|
+
...state,
|
|
81
|
+
showFileSelector: false,
|
|
82
|
+
atPosition: -1,
|
|
83
|
+
fileSearchQuery: "",
|
|
84
|
+
filteredFiles: [],
|
|
85
|
+
selectorJustUsed: true,
|
|
86
|
+
};
|
|
87
|
+
case "ACTIVATE_COMMAND_SELECTOR":
|
|
88
|
+
return {
|
|
89
|
+
...state,
|
|
90
|
+
showCommandSelector: true,
|
|
91
|
+
slashPosition: action.payload,
|
|
92
|
+
commandSearchQuery: "",
|
|
93
|
+
};
|
|
94
|
+
case "SET_COMMAND_SEARCH_QUERY":
|
|
95
|
+
return { ...state, commandSearchQuery: action.payload };
|
|
96
|
+
case "CANCEL_COMMAND_SELECTOR":
|
|
97
|
+
return {
|
|
98
|
+
...state,
|
|
99
|
+
showCommandSelector: false,
|
|
100
|
+
slashPosition: -1,
|
|
101
|
+
commandSearchQuery: "",
|
|
102
|
+
selectorJustUsed: true,
|
|
103
|
+
};
|
|
104
|
+
case "ACTIVATE_HISTORY_SEARCH":
|
|
105
|
+
return {
|
|
106
|
+
...state,
|
|
107
|
+
showHistorySearch: true,
|
|
108
|
+
historySearchQuery: "",
|
|
109
|
+
};
|
|
110
|
+
case "SET_HISTORY_SEARCH_QUERY":
|
|
111
|
+
return { ...state, historySearchQuery: action.payload };
|
|
112
|
+
case "CANCEL_HISTORY_SEARCH":
|
|
113
|
+
return {
|
|
114
|
+
...state,
|
|
115
|
+
showHistorySearch: false,
|
|
116
|
+
historySearchQuery: "",
|
|
117
|
+
selectorJustUsed: true,
|
|
118
|
+
};
|
|
119
|
+
case "ADD_IMAGE": {
|
|
120
|
+
const newImage = {
|
|
121
|
+
id: state.imageIdCounter,
|
|
122
|
+
path: action.payload.path,
|
|
123
|
+
mimeType: action.payload.mimeType,
|
|
124
|
+
};
|
|
125
|
+
return {
|
|
126
|
+
...state,
|
|
127
|
+
attachedImages: [...state.attachedImages, newImage],
|
|
128
|
+
imageIdCounter: state.imageIdCounter + 1,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
case "REMOVE_IMAGE":
|
|
132
|
+
return {
|
|
133
|
+
...state,
|
|
134
|
+
attachedImages: state.attachedImages.filter((img) => img.id !== action.payload),
|
|
135
|
+
};
|
|
136
|
+
case "CLEAR_IMAGES":
|
|
137
|
+
return { ...state, attachedImages: [] };
|
|
138
|
+
case "SET_SHOW_BACKGROUND_TASK_MANAGER":
|
|
139
|
+
return {
|
|
140
|
+
...state,
|
|
141
|
+
showBackgroundTaskManager: action.payload,
|
|
142
|
+
selectorJustUsed: !action.payload ? true : state.selectorJustUsed,
|
|
143
|
+
};
|
|
144
|
+
case "SET_SHOW_MCP_MANAGER":
|
|
145
|
+
return {
|
|
146
|
+
...state,
|
|
147
|
+
showMcpManager: action.payload,
|
|
148
|
+
selectorJustUsed: !action.payload ? true : state.selectorJustUsed,
|
|
149
|
+
};
|
|
150
|
+
case "SET_SHOW_REWIND_MANAGER":
|
|
151
|
+
return {
|
|
152
|
+
...state,
|
|
153
|
+
showRewindManager: action.payload,
|
|
154
|
+
selectorJustUsed: !action.payload ? true : state.selectorJustUsed,
|
|
155
|
+
};
|
|
156
|
+
case "SET_SHOW_HELP":
|
|
157
|
+
return {
|
|
158
|
+
...state,
|
|
159
|
+
showHelp: action.payload,
|
|
160
|
+
selectorJustUsed: !action.payload ? true : state.selectorJustUsed,
|
|
161
|
+
};
|
|
162
|
+
case "SET_SHOW_STATUS_COMMAND":
|
|
163
|
+
return {
|
|
164
|
+
...state,
|
|
165
|
+
showStatusCommand: action.payload,
|
|
166
|
+
selectorJustUsed: !action.payload ? true : state.selectorJustUsed,
|
|
167
|
+
};
|
|
168
|
+
case "SET_PERMISSION_MODE":
|
|
169
|
+
return { ...state, permissionMode: action.payload };
|
|
170
|
+
case "SET_SELECTOR_JUST_USED":
|
|
171
|
+
return { ...state, selectorJustUsed: action.payload };
|
|
172
|
+
case "COMPRESS_AND_INSERT_TEXT": {
|
|
173
|
+
let textToInsert = action.payload;
|
|
174
|
+
let newLongTextCounter = state.longTextCounter;
|
|
175
|
+
const newLongTextMap = { ...state.longTextMap };
|
|
176
|
+
if (textToInsert.length > 200) {
|
|
177
|
+
newLongTextCounter += 1;
|
|
178
|
+
const compressedLabel = `[LongText#${newLongTextCounter}]`;
|
|
179
|
+
newLongTextMap[compressedLabel] = textToInsert;
|
|
180
|
+
textToInsert = compressedLabel;
|
|
181
|
+
}
|
|
182
|
+
const beforeCursor = state.inputText.substring(0, state.cursorPosition);
|
|
183
|
+
const afterCursor = state.inputText.substring(state.cursorPosition);
|
|
184
|
+
const newText = beforeCursor + textToInsert + afterCursor;
|
|
185
|
+
const newCursorPosition = state.cursorPosition + textToInsert.length;
|
|
186
|
+
return {
|
|
187
|
+
...state,
|
|
188
|
+
inputText: newText,
|
|
189
|
+
cursorPosition: newCursorPosition,
|
|
190
|
+
longTextCounter: newLongTextCounter,
|
|
191
|
+
longTextMap: newLongTextMap,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
case "CLEAR_LONG_TEXT_MAP":
|
|
195
|
+
return { ...state, longTextMap: {} };
|
|
196
|
+
case "CLEAR_INPUT":
|
|
197
|
+
return {
|
|
198
|
+
...state,
|
|
199
|
+
inputText: "",
|
|
200
|
+
cursorPosition: 0,
|
|
201
|
+
};
|
|
202
|
+
case "START_PASTE":
|
|
203
|
+
return {
|
|
204
|
+
...state,
|
|
205
|
+
isPasting: true,
|
|
206
|
+
pasteBuffer: action.payload.buffer,
|
|
207
|
+
initialPasteCursorPosition: action.payload.cursorPosition,
|
|
208
|
+
};
|
|
209
|
+
case "APPEND_PASTE_BUFFER":
|
|
210
|
+
return {
|
|
211
|
+
...state,
|
|
212
|
+
pasteBuffer: state.pasteBuffer + action.payload,
|
|
213
|
+
};
|
|
214
|
+
case "END_PASTE":
|
|
215
|
+
return {
|
|
216
|
+
...state,
|
|
217
|
+
isPasting: false,
|
|
218
|
+
pasteBuffer: "",
|
|
219
|
+
};
|
|
220
|
+
case "ADD_IMAGE_AND_INSERT_PLACEHOLDER": {
|
|
221
|
+
const newImage = {
|
|
222
|
+
id: state.imageIdCounter,
|
|
223
|
+
path: action.payload.path,
|
|
224
|
+
mimeType: action.payload.mimeType,
|
|
225
|
+
};
|
|
226
|
+
const placeholder = `[Image #${newImage.id}]`;
|
|
227
|
+
const beforeCursor = state.inputText.substring(0, state.cursorPosition);
|
|
228
|
+
const afterCursor = state.inputText.substring(state.cursorPosition);
|
|
229
|
+
const newText = beforeCursor + placeholder + afterCursor;
|
|
230
|
+
const newCursorPosition = state.cursorPosition + placeholder.length;
|
|
231
|
+
return {
|
|
232
|
+
...state,
|
|
233
|
+
attachedImages: [...state.attachedImages, newImage],
|
|
234
|
+
imageIdCounter: state.imageIdCounter + 1,
|
|
235
|
+
inputText: newText,
|
|
236
|
+
cursorPosition: newCursorPosition,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
default:
|
|
240
|
+
return state;
|
|
241
|
+
}
|
|
242
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-code",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "CLI-based code assistant powered by AI, built with React and Ink",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"react": "^19.2.4",
|
|
40
40
|
"react-dom": "19.2.4",
|
|
41
41
|
"yargs": "^17.7.2",
|
|
42
|
-
"wave-agent-sdk": "0.8.
|
|
42
|
+
"wave-agent-sdk": "0.8.2"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/react": "^19.1.8",
|
|
@@ -95,7 +95,7 @@ export const ChatInterface: React.FC = () => {
|
|
|
95
95
|
<MessageList
|
|
96
96
|
messages={messages}
|
|
97
97
|
isExpanded={isExpanded}
|
|
98
|
-
|
|
98
|
+
forceStatic={isConfirmationVisible && isConfirmationTooTall}
|
|
99
99
|
version={version}
|
|
100
100
|
workdir={workdir}
|
|
101
101
|
model={model}
|
|
@@ -7,7 +7,7 @@ import { MessageBlockItem } from "./MessageBlockItem.js";
|
|
|
7
7
|
export interface MessageListProps {
|
|
8
8
|
messages: Message[];
|
|
9
9
|
isExpanded?: boolean;
|
|
10
|
-
|
|
10
|
+
forceStatic?: boolean;
|
|
11
11
|
version?: string;
|
|
12
12
|
workdir?: string;
|
|
13
13
|
model?: string;
|
|
@@ -17,7 +17,7 @@ export const MessageList = React.memo(
|
|
|
17
17
|
({
|
|
18
18
|
messages,
|
|
19
19
|
isExpanded = false,
|
|
20
|
-
|
|
20
|
+
forceStatic = false,
|
|
21
21
|
version,
|
|
22
22
|
workdir,
|
|
23
23
|
model,
|
|
@@ -54,7 +54,7 @@ export const MessageList = React.memo(
|
|
|
54
54
|
message,
|
|
55
55
|
isLastMessage: messageIndex === messages.length - 1,
|
|
56
56
|
// Unique key for each block to help Static component
|
|
57
|
-
key: `${message.id
|
|
57
|
+
key: `${message.id}-${blockIndex}`,
|
|
58
58
|
}));
|
|
59
59
|
});
|
|
60
60
|
|
|
@@ -62,6 +62,7 @@ export const MessageList = React.memo(
|
|
|
62
62
|
const blocksWithStatus = allBlocks.map((item) => {
|
|
63
63
|
const { block, isLastMessage } = item;
|
|
64
64
|
const isDynamic =
|
|
65
|
+
!forceStatic &&
|
|
65
66
|
isLastMessage &&
|
|
66
67
|
((block.type === "tool" && block.stage !== "end") ||
|
|
67
68
|
(block.type === "bang" && block.isRunning));
|
|
@@ -69,9 +70,7 @@ export const MessageList = React.memo(
|
|
|
69
70
|
});
|
|
70
71
|
|
|
71
72
|
const staticBlocks = blocksWithStatus.filter((b) => !b.isDynamic);
|
|
72
|
-
const dynamicBlocks =
|
|
73
|
-
? []
|
|
74
|
-
: blocksWithStatus.filter((b) => b.isDynamic);
|
|
73
|
+
const dynamicBlocks = blocksWithStatus.filter((b) => b.isDynamic);
|
|
75
74
|
|
|
76
75
|
const staticItems = [
|
|
77
76
|
{ isWelcome: true, key: "welcome", block: undefined, message: undefined },
|
package/src/contexts/useChat.tsx
CHANGED
|
@@ -141,7 +141,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
141
141
|
|
|
142
142
|
// AI State
|
|
143
143
|
const [messages, setMessages] = useState<Message[]>([]);
|
|
144
|
-
const messagesUpdateTimerRef = useRef<NodeJS.Timeout | null>(null);
|
|
145
144
|
|
|
146
145
|
const [isLoading, setIsLoading] = useState(false);
|
|
147
146
|
const [latestTotalTokens, setlatestTotalTokens] = useState(0);
|
|
@@ -243,15 +242,8 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
243
242
|
const initializeAgent = async () => {
|
|
244
243
|
const callbacks: AgentCallbacks = {
|
|
245
244
|
onMessagesChange: () => {
|
|
246
|
-
if (!isExpandedRef.current) {
|
|
247
|
-
|
|
248
|
-
messagesUpdateTimerRef.current = setTimeout(() => {
|
|
249
|
-
if (agentRef.current) {
|
|
250
|
-
setMessages([...agentRef.current.messages]);
|
|
251
|
-
}
|
|
252
|
-
messagesUpdateTimerRef.current = null;
|
|
253
|
-
}, 100);
|
|
254
|
-
}
|
|
245
|
+
if (!isExpandedRef.current && agentRef.current) {
|
|
246
|
+
setMessages([...agentRef.current.messages]);
|
|
255
247
|
}
|
|
256
248
|
},
|
|
257
249
|
onServersChange: (servers) => {
|
|
@@ -374,9 +366,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
374
366
|
// Cleanup on unmount
|
|
375
367
|
useEffect(() => {
|
|
376
368
|
return () => {
|
|
377
|
-
if (messagesUpdateTimerRef.current) {
|
|
378
|
-
clearTimeout(messagesUpdateTimerRef.current);
|
|
379
|
-
}
|
|
380
369
|
if (agentRef.current) {
|
|
381
370
|
try {
|
|
382
371
|
// Display usage summary before cleanup
|
|
@@ -580,10 +569,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
580
569
|
setIsExpanded((prev) => {
|
|
581
570
|
const newExpanded = !prev;
|
|
582
571
|
if (newExpanded) {
|
|
583
|
-
if (messagesUpdateTimerRef.current) {
|
|
584
|
-
clearTimeout(messagesUpdateTimerRef.current);
|
|
585
|
-
messagesUpdateTimerRef.current = null;
|
|
586
|
-
}
|
|
587
572
|
// Transitioning to EXPANDED: Freeze the current view
|
|
588
573
|
// Deep copy the last message to ensure it doesn't update if the agent is still writing to it
|
|
589
574
|
setMessages((currentMessages) => {
|