wave-code 0.8.4 → 0.9.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.
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +26 -7
- package/dist/components/FileSelector.d.ts +1 -0
- package/dist/components/FileSelector.d.ts.map +1 -1
- package/dist/components/FileSelector.js +3 -3
- package/dist/components/HelpView.d.ts.map +1 -1
- package/dist/components/HelpView.js +1 -0
- package/dist/components/HistorySearch.d.ts +2 -1
- package/dist/components/HistorySearch.d.ts.map +1 -1
- package/dist/components/HistorySearch.js +1 -1
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +8 -6
- package/dist/components/MessageBlockItem.d.ts.map +1 -1
- package/dist/components/MessageBlockItem.js +1 -1
- package/dist/components/MessageList.d.ts +2 -1
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +14 -4
- package/dist/components/QueuedMessageList.d.ts +3 -0
- package/dist/components/QueuedMessageList.d.ts.map +1 -0
- package/dist/components/QueuedMessageList.js +17 -0
- package/dist/components/ReasoningDisplay.d.ts +1 -0
- package/dist/components/ReasoningDisplay.d.ts.map +1 -1
- package/dist/components/ReasoningDisplay.js +3 -3
- package/dist/components/ToolDisplay.js +1 -1
- package/dist/contexts/useChat.d.ts +7 -0
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +17 -1
- package/dist/hooks/useInputManager.d.ts +6 -5
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +18 -16
- package/dist/managers/inputHandlers.d.ts +7 -4
- package/dist/managers/inputHandlers.d.ts.map +1 -1
- package/dist/managers/inputHandlers.js +184 -46
- package/dist/managers/inputReducer.d.ts +18 -2
- package/dist/managers/inputReducer.d.ts.map +1 -1
- package/dist/managers/inputReducer.js +92 -3
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +13 -1
- package/package.json +2 -2
- package/src/components/ChatInterface.tsx +42 -15
- package/src/components/FileSelector.tsx +13 -3
- package/src/components/HelpView.tsx +1 -0
- package/src/components/HistorySearch.tsx +2 -2
- package/src/components/InputBox.tsx +21 -17
- package/src/components/MessageBlockItem.tsx +8 -3
- package/src/components/MessageList.tsx +16 -3
- package/src/components/QueuedMessageList.tsx +31 -0
- package/src/components/ReasoningDisplay.tsx +8 -2
- package/src/components/ToolDisplay.tsx +2 -2
- package/src/contexts/useChat.tsx +29 -1
- package/src/hooks/useInputManager.ts +22 -31
- package/src/managers/inputHandlers.ts +223 -60
- package/src/managers/inputReducer.ts +104 -6
- package/src/utils/logger.ts +15 -1
|
@@ -30,18 +30,12 @@ export const handleSubmit = async (
|
|
|
30
30
|
state: InputState,
|
|
31
31
|
dispatch: React.Dispatch<InputAction>,
|
|
32
32
|
callbacks: Partial<InputManagerCallbacks>,
|
|
33
|
-
isLoading: boolean = false,
|
|
34
|
-
isCommandRunning: boolean = false,
|
|
35
33
|
attachedImagesOverride?: Array<{
|
|
36
34
|
id: number;
|
|
37
35
|
path: string;
|
|
38
36
|
mimeType: string;
|
|
39
37
|
}>,
|
|
40
38
|
): Promise<void> => {
|
|
41
|
-
if (isLoading || isCommandRunning) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
39
|
if (state.inputText.trim()) {
|
|
46
40
|
const imageRegex = /\[Image #(\d+)\]/g;
|
|
47
41
|
const matches = [...state.inputText.matchAll(imageRegex)];
|
|
@@ -60,7 +54,11 @@ export const handleSubmit = async (
|
|
|
60
54
|
let cleanContent = state.inputText.replace(imageRegex, "").trim();
|
|
61
55
|
cleanContent = expandLongTextPlaceholders(cleanContent, state.longTextMap);
|
|
62
56
|
|
|
63
|
-
PromptHistoryManager.addEntry(
|
|
57
|
+
PromptHistoryManager.addEntry(
|
|
58
|
+
cleanContent,
|
|
59
|
+
callbacks.sessionId,
|
|
60
|
+
state.longTextMap,
|
|
61
|
+
).catch((err: unknown) => {
|
|
64
62
|
callbacks.logger?.error("Failed to save prompt history", err);
|
|
65
63
|
});
|
|
66
64
|
|
|
@@ -69,7 +67,7 @@ export const handleSubmit = async (
|
|
|
69
67
|
referencedImages.length > 0 ? referencedImages : undefined,
|
|
70
68
|
);
|
|
71
69
|
dispatch({ type: "CLEAR_INPUT" });
|
|
72
|
-
|
|
70
|
+
dispatch({ type: "RESET_HISTORY_NAVIGATION" });
|
|
73
71
|
dispatch({ type: "CLEAR_LONG_TEXT_MAP" });
|
|
74
72
|
}
|
|
75
73
|
};
|
|
@@ -114,6 +112,72 @@ export const cyclePermissionMode = (
|
|
|
114
112
|
callbacks.onPermissionModeChange?.(nextMode);
|
|
115
113
|
};
|
|
116
114
|
|
|
115
|
+
const SELECTOR_TRIGGERS = [
|
|
116
|
+
{
|
|
117
|
+
char: "@",
|
|
118
|
+
type: "ACTIVATE_FILE_SELECTOR",
|
|
119
|
+
shouldActivate: (char: string, pos: number, text: string) =>
|
|
120
|
+
char === "@" && (pos === 1 || /\s/.test(text[pos - 2])),
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
char: "/",
|
|
124
|
+
type: "ACTIVATE_COMMAND_SELECTOR",
|
|
125
|
+
shouldActivate: (
|
|
126
|
+
char: string,
|
|
127
|
+
pos: number,
|
|
128
|
+
text: string,
|
|
129
|
+
state: InputState,
|
|
130
|
+
) =>
|
|
131
|
+
char === "/" &&
|
|
132
|
+
!state.showFileSelector &&
|
|
133
|
+
(pos === 1 || /\s/.test(text[pos - 2])),
|
|
134
|
+
},
|
|
135
|
+
] as const;
|
|
136
|
+
|
|
137
|
+
const getProjectedState = (state: InputState, char: string) => {
|
|
138
|
+
const beforeCursor = state.inputText.substring(0, state.cursorPosition);
|
|
139
|
+
const afterCursor = state.inputText.substring(state.cursorPosition);
|
|
140
|
+
const newInputText = beforeCursor + char + afterCursor;
|
|
141
|
+
const newCursorPosition = state.cursorPosition + char.length;
|
|
142
|
+
return { newInputText, newCursorPosition };
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export const getAtSelectorPosition = (
|
|
146
|
+
text: string,
|
|
147
|
+
cursorPosition: number,
|
|
148
|
+
): number => {
|
|
149
|
+
let i = cursorPosition - 1;
|
|
150
|
+
while (i >= 0 && !/\s/.test(text[i])) {
|
|
151
|
+
if (text[i] === "@") {
|
|
152
|
+
// Check if this @ is at the start or preceded by whitespace
|
|
153
|
+
if (i === 0 || /\s/.test(text[i - 1])) {
|
|
154
|
+
return i;
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
i--;
|
|
159
|
+
}
|
|
160
|
+
return -1;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export const getSlashSelectorPosition = (
|
|
164
|
+
text: string,
|
|
165
|
+
cursorPosition: number,
|
|
166
|
+
): number => {
|
|
167
|
+
let i = cursorPosition - 1;
|
|
168
|
+
while (i >= 0 && !/\s/.test(text[i])) {
|
|
169
|
+
if (text[i] === "/") {
|
|
170
|
+
// Check if this / is at the start or preceded by whitespace
|
|
171
|
+
if (i === 0 || /\s/.test(text[i - 1])) {
|
|
172
|
+
return i;
|
|
173
|
+
}
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
i--;
|
|
177
|
+
}
|
|
178
|
+
return -1;
|
|
179
|
+
};
|
|
180
|
+
|
|
117
181
|
export const updateSearchQueriesForActiveSelectors = (
|
|
118
182
|
state: InputState,
|
|
119
183
|
dispatch: React.Dispatch<InputAction>,
|
|
@@ -133,26 +197,58 @@ export const updateSearchQueriesForActiveSelectors = (
|
|
|
133
197
|
}
|
|
134
198
|
};
|
|
135
199
|
|
|
136
|
-
export const
|
|
200
|
+
export const processSelectorInput = (
|
|
137
201
|
state: InputState,
|
|
138
202
|
dispatch: React.Dispatch<InputAction>,
|
|
139
203
|
char: string,
|
|
140
|
-
cursorPosition: number,
|
|
141
|
-
inputText: string,
|
|
142
204
|
): void => {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
205
|
+
const { newInputText, newCursorPosition } = getProjectedState(state, char);
|
|
206
|
+
|
|
207
|
+
const trigger = SELECTOR_TRIGGERS.find((t) =>
|
|
208
|
+
t.shouldActivate(char, newCursorPosition, newInputText, state),
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
if (trigger) {
|
|
146
212
|
dispatch({
|
|
147
|
-
type:
|
|
148
|
-
payload:
|
|
149
|
-
});
|
|
213
|
+
type: trigger.type,
|
|
214
|
+
payload: newCursorPosition - 1,
|
|
215
|
+
} as InputAction);
|
|
150
216
|
} else {
|
|
217
|
+
const atPos = getAtSelectorPosition(newInputText, newCursorPosition);
|
|
218
|
+
let showFileSelector = state.showFileSelector;
|
|
219
|
+
let atPosition = state.atPosition;
|
|
220
|
+
if (atPos !== -1 && !state.showFileSelector) {
|
|
221
|
+
dispatch({
|
|
222
|
+
type: "ACTIVATE_FILE_SELECTOR",
|
|
223
|
+
payload: atPos,
|
|
224
|
+
});
|
|
225
|
+
showFileSelector = true;
|
|
226
|
+
atPosition = atPos;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const slashPos = getSlashSelectorPosition(newInputText, newCursorPosition);
|
|
230
|
+
let showCommandSelector = state.showCommandSelector;
|
|
231
|
+
let slashPosition = state.slashPosition;
|
|
232
|
+
if (slashPos !== -1 && !state.showCommandSelector) {
|
|
233
|
+
dispatch({
|
|
234
|
+
type: "ACTIVATE_COMMAND_SELECTOR",
|
|
235
|
+
payload: slashPos,
|
|
236
|
+
});
|
|
237
|
+
showCommandSelector = true;
|
|
238
|
+
slashPosition = slashPos;
|
|
239
|
+
}
|
|
240
|
+
|
|
151
241
|
updateSearchQueriesForActiveSelectors(
|
|
152
|
-
|
|
242
|
+
{
|
|
243
|
+
...state,
|
|
244
|
+
showFileSelector,
|
|
245
|
+
atPosition,
|
|
246
|
+
showCommandSelector,
|
|
247
|
+
slashPosition,
|
|
248
|
+
},
|
|
153
249
|
dispatch,
|
|
154
|
-
|
|
155
|
-
|
|
250
|
+
newInputText,
|
|
251
|
+
newCursorPosition,
|
|
156
252
|
);
|
|
157
253
|
}
|
|
158
254
|
};
|
|
@@ -184,23 +280,19 @@ export const handlePasteInput = (
|
|
|
184
280
|
char = "!";
|
|
185
281
|
}
|
|
186
282
|
|
|
187
|
-
|
|
283
|
+
dispatch({ type: "RESET_HISTORY_NAVIGATION" });
|
|
188
284
|
dispatch({ type: "INSERT_TEXT", payload: char });
|
|
189
285
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const afterCursor = state.inputText.substring(state.cursorPosition);
|
|
194
|
-
const newInputText = beforeCursor + char + afterCursor;
|
|
286
|
+
processSelectorInput(state, dispatch, char);
|
|
287
|
+
}
|
|
288
|
+
};
|
|
195
289
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
newCursorPosition,
|
|
201
|
-
newInputText,
|
|
202
|
-
);
|
|
290
|
+
export const getWordEnd = (text: string, startPos: number): number => {
|
|
291
|
+
let i = startPos;
|
|
292
|
+
while (i < text.length && !/\s/.test(text[i])) {
|
|
293
|
+
i++;
|
|
203
294
|
}
|
|
295
|
+
return i;
|
|
204
296
|
};
|
|
205
297
|
|
|
206
298
|
export const handleCommandSelect = (
|
|
@@ -210,9 +302,10 @@ export const handleCommandSelect = (
|
|
|
210
302
|
command: string,
|
|
211
303
|
) => {
|
|
212
304
|
if (state.slashPosition >= 0) {
|
|
305
|
+
const wordEnd = getWordEnd(state.inputText, state.slashPosition);
|
|
213
306
|
const beforeSlash = state.inputText.substring(0, state.slashPosition);
|
|
214
|
-
const
|
|
215
|
-
const newInput = beforeSlash +
|
|
307
|
+
const afterWord = state.inputText.substring(wordEnd);
|
|
308
|
+
const newInput = beforeSlash + afterWord;
|
|
216
309
|
const newCursorPosition = beforeSlash.length;
|
|
217
310
|
|
|
218
311
|
dispatch({ type: "SET_INPUT_TEXT", payload: newInput });
|
|
@@ -264,10 +357,11 @@ export const handleFileSelect = (
|
|
|
264
357
|
filePath: string,
|
|
265
358
|
) => {
|
|
266
359
|
if (state.atPosition >= 0) {
|
|
360
|
+
const wordEnd = getWordEnd(state.inputText, state.atPosition);
|
|
267
361
|
const beforeAt = state.inputText.substring(0, state.atPosition);
|
|
268
|
-
const
|
|
269
|
-
const newInput = beforeAt +
|
|
270
|
-
const newCursorPosition = beforeAt.length + filePath.length +
|
|
362
|
+
const afterWord = state.inputText.substring(wordEnd);
|
|
363
|
+
const newInput = beforeAt + `@${filePath} ` + afterWord;
|
|
364
|
+
const newCursorPosition = beforeAt.length + filePath.length + 2;
|
|
271
365
|
|
|
272
366
|
dispatch({ type: "SET_INPUT_TEXT", payload: newInput });
|
|
273
367
|
dispatch({ type: "SET_CURSOR_POSITION", payload: newCursorPosition });
|
|
@@ -343,6 +437,26 @@ export const handleSelectorInput = (
|
|
|
343
437
|
return true;
|
|
344
438
|
}
|
|
345
439
|
|
|
440
|
+
if (key.leftArrow) {
|
|
441
|
+
const newCursorPosition = state.cursorPosition - 1;
|
|
442
|
+
dispatch({ type: "MOVE_CURSOR", payload: -1 });
|
|
443
|
+
checkForAtDeletion(state, dispatch, newCursorPosition);
|
|
444
|
+
checkForSlashDeletion(state, dispatch, newCursorPosition);
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (key.rightArrow) {
|
|
449
|
+
const newCursorPosition = state.cursorPosition + 1;
|
|
450
|
+
dispatch({ type: "MOVE_CURSOR", payload: 1 });
|
|
451
|
+
checkForAtDeletion(state, dispatch, newCursorPosition);
|
|
452
|
+
checkForSlashDeletion(state, dispatch, newCursorPosition);
|
|
453
|
+
return true;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (input === " " && state.showFileSelector) {
|
|
457
|
+
dispatch({ type: "CANCEL_FILE_SELECTOR" });
|
|
458
|
+
}
|
|
459
|
+
|
|
346
460
|
if (
|
|
347
461
|
input &&
|
|
348
462
|
!key.ctrl &&
|
|
@@ -358,19 +472,7 @@ export const handleSelectorInput = (
|
|
|
358
472
|
) {
|
|
359
473
|
dispatch({ type: "INSERT_TEXT", payload: input });
|
|
360
474
|
|
|
361
|
-
|
|
362
|
-
const newCursorPosition = state.cursorPosition + input.length;
|
|
363
|
-
const beforeCursor = state.inputText.substring(0, state.cursorPosition);
|
|
364
|
-
const afterCursor = state.inputText.substring(state.cursorPosition);
|
|
365
|
-
const newInputText = beforeCursor + input + afterCursor;
|
|
366
|
-
|
|
367
|
-
handleSpecialCharInput(
|
|
368
|
-
state,
|
|
369
|
-
dispatch,
|
|
370
|
-
input,
|
|
371
|
-
newCursorPosition,
|
|
372
|
-
newInputText,
|
|
373
|
-
);
|
|
475
|
+
processSelectorInput(state, dispatch, input);
|
|
374
476
|
return true;
|
|
375
477
|
}
|
|
376
478
|
|
|
@@ -383,12 +485,10 @@ export const handleNormalInput = async (
|
|
|
383
485
|
callbacks: Partial<InputManagerCallbacks>,
|
|
384
486
|
input: string,
|
|
385
487
|
key: Key,
|
|
386
|
-
isLoading: boolean = false,
|
|
387
|
-
isCommandRunning: boolean = false,
|
|
388
488
|
clearImages?: () => void,
|
|
389
489
|
): Promise<boolean> => {
|
|
390
490
|
if (key.return) {
|
|
391
|
-
await handleSubmit(state, dispatch, callbacks
|
|
491
|
+
await handleSubmit(state, dispatch, callbacks);
|
|
392
492
|
clearImages?.();
|
|
393
493
|
return true;
|
|
394
494
|
}
|
|
@@ -398,18 +498,86 @@ export const handleNormalInput = async (
|
|
|
398
498
|
dispatch({ type: "CANCEL_FILE_SELECTOR" });
|
|
399
499
|
} else if (state.showCommandSelector) {
|
|
400
500
|
dispatch({ type: "CANCEL_COMMAND_SELECTOR" });
|
|
501
|
+
} else if (state.historyIndex !== -1) {
|
|
502
|
+
dispatch({ type: "RESET_HISTORY_NAVIGATION" });
|
|
503
|
+
}
|
|
504
|
+
return true;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (key.upArrow) {
|
|
508
|
+
if (state.history.length === 0) {
|
|
509
|
+
const history = await PromptHistoryManager.getHistory(
|
|
510
|
+
callbacks.sessionId,
|
|
511
|
+
);
|
|
512
|
+
dispatch({ type: "SET_HISTORY_ENTRIES", payload: history });
|
|
401
513
|
}
|
|
514
|
+
dispatch({ type: "NAVIGATE_HISTORY", payload: "up" });
|
|
515
|
+
return true;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (key.downArrow) {
|
|
519
|
+
dispatch({ type: "NAVIGATE_HISTORY", payload: "down" });
|
|
402
520
|
return true;
|
|
403
521
|
}
|
|
404
522
|
|
|
405
523
|
if (key.backspace || key.delete) {
|
|
406
524
|
if (state.cursorPosition > 0) {
|
|
407
525
|
const newCursorPosition = state.cursorPosition - 1;
|
|
526
|
+
const beforeCursor = state.inputText.substring(
|
|
527
|
+
0,
|
|
528
|
+
state.cursorPosition - 1,
|
|
529
|
+
);
|
|
530
|
+
const afterCursor = state.inputText.substring(state.cursorPosition);
|
|
531
|
+
const newInputText = beforeCursor + afterCursor;
|
|
532
|
+
|
|
408
533
|
dispatch({ type: "DELETE_CHAR" });
|
|
409
|
-
|
|
534
|
+
dispatch({ type: "RESET_HISTORY_NAVIGATION" });
|
|
410
535
|
|
|
411
536
|
checkForAtDeletion(state, dispatch, newCursorPosition);
|
|
412
537
|
checkForSlashDeletion(state, dispatch, newCursorPosition);
|
|
538
|
+
|
|
539
|
+
// Reactivate file selector if cursor is now within an @word
|
|
540
|
+
const atPos = getAtSelectorPosition(newInputText, newCursorPosition);
|
|
541
|
+
let showFileSelector = state.showFileSelector;
|
|
542
|
+
let atPosition = state.atPosition;
|
|
543
|
+
if (atPos !== -1 && !state.showFileSelector) {
|
|
544
|
+
dispatch({
|
|
545
|
+
type: "ACTIVATE_FILE_SELECTOR",
|
|
546
|
+
payload: atPos,
|
|
547
|
+
});
|
|
548
|
+
showFileSelector = true;
|
|
549
|
+
atPosition = atPos;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const slashPos = getSlashSelectorPosition(
|
|
553
|
+
newInputText,
|
|
554
|
+
newCursorPosition,
|
|
555
|
+
);
|
|
556
|
+
let showCommandSelector = state.showCommandSelector;
|
|
557
|
+
let slashPosition = state.slashPosition;
|
|
558
|
+
if (slashPos !== -1 && !state.showCommandSelector) {
|
|
559
|
+
dispatch({
|
|
560
|
+
type: "ACTIVATE_COMMAND_SELECTOR",
|
|
561
|
+
payload: slashPos,
|
|
562
|
+
});
|
|
563
|
+
showCommandSelector = true;
|
|
564
|
+
slashPosition = slashPos;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
updateSearchQueriesForActiveSelectors(
|
|
568
|
+
{
|
|
569
|
+
...state,
|
|
570
|
+
inputText: newInputText,
|
|
571
|
+
cursorPosition: newCursorPosition,
|
|
572
|
+
showFileSelector,
|
|
573
|
+
atPosition,
|
|
574
|
+
showCommandSelector,
|
|
575
|
+
slashPosition,
|
|
576
|
+
},
|
|
577
|
+
dispatch,
|
|
578
|
+
newInputText,
|
|
579
|
+
newCursorPosition,
|
|
580
|
+
);
|
|
413
581
|
}
|
|
414
582
|
return true;
|
|
415
583
|
}
|
|
@@ -468,8 +636,6 @@ export const handleInput = async (
|
|
|
468
636
|
callbacks: Partial<InputManagerCallbacks>,
|
|
469
637
|
input: string,
|
|
470
638
|
key: Key,
|
|
471
|
-
isLoading: boolean = false,
|
|
472
|
-
isCommandRunning: boolean = false,
|
|
473
639
|
clearImages?: () => void,
|
|
474
640
|
): Promise<boolean> => {
|
|
475
641
|
if (state.selectorJustUsed) {
|
|
@@ -478,7 +644,6 @@ export const handleInput = async (
|
|
|
478
644
|
|
|
479
645
|
if (key.escape) {
|
|
480
646
|
if (
|
|
481
|
-
(isLoading || isCommandRunning) &&
|
|
482
647
|
!(
|
|
483
648
|
state.showFileSelector ||
|
|
484
649
|
state.showCommandSelector ||
|
|
@@ -552,8 +717,6 @@ export const handleInput = async (
|
|
|
552
717
|
callbacks,
|
|
553
718
|
input,
|
|
554
719
|
key,
|
|
555
|
-
isLoading,
|
|
556
|
-
isCommandRunning,
|
|
557
720
|
clearImages,
|
|
558
721
|
);
|
|
559
722
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FileItem, PermissionMode, Logger } from "wave-agent-sdk";
|
|
1
|
+
import { FileItem, PermissionMode, Logger, PromptEntry } from "wave-agent-sdk";
|
|
2
2
|
|
|
3
3
|
export interface AttachedImage {
|
|
4
4
|
id: number;
|
|
@@ -35,8 +35,8 @@ export interface InputManagerCallbacks {
|
|
|
35
35
|
onAbortMessage?: () => void;
|
|
36
36
|
onClearMessages?: () => void;
|
|
37
37
|
onBackgroundCurrentTask?: () => void;
|
|
38
|
-
onResetHistoryNavigation?: () => void;
|
|
39
38
|
onPermissionModeChange?: (mode: PermissionMode) => void;
|
|
39
|
+
sessionId?: string;
|
|
40
40
|
logger?: Logger;
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -66,6 +66,11 @@ export interface InputState {
|
|
|
66
66
|
isPasting: boolean;
|
|
67
67
|
pasteBuffer: string;
|
|
68
68
|
initialPasteCursorPosition: number;
|
|
69
|
+
history: PromptEntry[];
|
|
70
|
+
historyIndex: number;
|
|
71
|
+
originalInputText: string;
|
|
72
|
+
originalLongTextMap: Record<string, string>;
|
|
73
|
+
isFileSearching: boolean;
|
|
69
74
|
}
|
|
70
75
|
|
|
71
76
|
export const initialState: InputState = {
|
|
@@ -94,6 +99,11 @@ export const initialState: InputState = {
|
|
|
94
99
|
isPasting: false,
|
|
95
100
|
pasteBuffer: "",
|
|
96
101
|
initialPasteCursorPosition: 0,
|
|
102
|
+
history: [],
|
|
103
|
+
historyIndex: -1,
|
|
104
|
+
originalInputText: "",
|
|
105
|
+
originalLongTextMap: {},
|
|
106
|
+
isFileSearching: false,
|
|
97
107
|
};
|
|
98
108
|
|
|
99
109
|
export type InputAction =
|
|
@@ -131,7 +141,11 @@ export type InputAction =
|
|
|
131
141
|
| {
|
|
132
142
|
type: "ADD_IMAGE_AND_INSERT_PLACEHOLDER";
|
|
133
143
|
payload: { path: string; mimeType: string };
|
|
134
|
-
}
|
|
144
|
+
}
|
|
145
|
+
| { type: "SET_HISTORY_ENTRIES"; payload: PromptEntry[] }
|
|
146
|
+
| { type: "NAVIGATE_HISTORY"; payload: "up" | "down" }
|
|
147
|
+
| { type: "RESET_HISTORY_NAVIGATION" }
|
|
148
|
+
| { type: "SELECT_HISTORY_ENTRY"; payload: PromptEntry };
|
|
135
149
|
|
|
136
150
|
export function inputReducer(
|
|
137
151
|
state: InputState,
|
|
@@ -139,7 +153,11 @@ export function inputReducer(
|
|
|
139
153
|
): InputState {
|
|
140
154
|
switch (action.type) {
|
|
141
155
|
case "SET_INPUT_TEXT":
|
|
142
|
-
return {
|
|
156
|
+
return {
|
|
157
|
+
...state,
|
|
158
|
+
inputText: action.payload,
|
|
159
|
+
historyIndex: -1,
|
|
160
|
+
};
|
|
143
161
|
case "SET_CURSOR_POSITION":
|
|
144
162
|
return {
|
|
145
163
|
...state,
|
|
@@ -157,6 +175,7 @@ export function inputReducer(
|
|
|
157
175
|
...state,
|
|
158
176
|
inputText: newText,
|
|
159
177
|
cursorPosition: newCursorPosition,
|
|
178
|
+
historyIndex: -1,
|
|
160
179
|
};
|
|
161
180
|
}
|
|
162
181
|
case "DELETE_CHAR": {
|
|
@@ -172,6 +191,7 @@ export function inputReducer(
|
|
|
172
191
|
...state,
|
|
173
192
|
inputText: newText,
|
|
174
193
|
cursorPosition: newCursorPosition,
|
|
194
|
+
historyIndex: -1,
|
|
175
195
|
};
|
|
176
196
|
}
|
|
177
197
|
return state;
|
|
@@ -190,11 +210,20 @@ export function inputReducer(
|
|
|
190
210
|
atPosition: action.payload,
|
|
191
211
|
fileSearchQuery: "",
|
|
192
212
|
filteredFiles: [],
|
|
213
|
+
isFileSearching: true,
|
|
193
214
|
};
|
|
194
215
|
case "SET_FILE_SEARCH_QUERY":
|
|
195
|
-
return {
|
|
216
|
+
return {
|
|
217
|
+
...state,
|
|
218
|
+
fileSearchQuery: action.payload,
|
|
219
|
+
isFileSearching: true,
|
|
220
|
+
};
|
|
196
221
|
case "SET_FILTERED_FILES":
|
|
197
|
-
return {
|
|
222
|
+
return {
|
|
223
|
+
...state,
|
|
224
|
+
filteredFiles: action.payload,
|
|
225
|
+
isFileSearching: false,
|
|
226
|
+
};
|
|
198
227
|
case "CANCEL_FILE_SELECTOR":
|
|
199
228
|
return {
|
|
200
229
|
...state,
|
|
@@ -203,6 +232,7 @@ export function inputReducer(
|
|
|
203
232
|
fileSearchQuery: "",
|
|
204
233
|
filteredFiles: [],
|
|
205
234
|
selectorJustUsed: true,
|
|
235
|
+
isFileSearching: false,
|
|
206
236
|
};
|
|
207
237
|
case "ACTIVATE_COMMAND_SELECTOR":
|
|
208
238
|
return {
|
|
@@ -314,6 +344,7 @@ export function inputReducer(
|
|
|
314
344
|
cursorPosition: newCursorPosition,
|
|
315
345
|
longTextCounter: newLongTextCounter,
|
|
316
346
|
longTextMap: newLongTextMap,
|
|
347
|
+
historyIndex: -1,
|
|
317
348
|
};
|
|
318
349
|
}
|
|
319
350
|
case "CLEAR_LONG_TEXT_MAP":
|
|
@@ -323,6 +354,7 @@ export function inputReducer(
|
|
|
323
354
|
...state,
|
|
324
355
|
inputText: "",
|
|
325
356
|
cursorPosition: 0,
|
|
357
|
+
historyIndex: -1,
|
|
326
358
|
};
|
|
327
359
|
case "START_PASTE":
|
|
328
360
|
return {
|
|
@@ -359,8 +391,74 @@ export function inputReducer(
|
|
|
359
391
|
imageIdCounter: state.imageIdCounter + 1,
|
|
360
392
|
inputText: newText,
|
|
361
393
|
cursorPosition: newCursorPosition,
|
|
394
|
+
historyIndex: -1,
|
|
362
395
|
};
|
|
363
396
|
}
|
|
397
|
+
case "SET_HISTORY_ENTRIES":
|
|
398
|
+
return { ...state, history: action.payload };
|
|
399
|
+
case "NAVIGATE_HISTORY": {
|
|
400
|
+
const direction = action.payload;
|
|
401
|
+
let newIndex = state.historyIndex;
|
|
402
|
+
let newOriginalInputText = state.originalInputText;
|
|
403
|
+
let newOriginalLongTextMap = state.originalLongTextMap;
|
|
404
|
+
|
|
405
|
+
if (direction === "up") {
|
|
406
|
+
if (newIndex === -1) {
|
|
407
|
+
newOriginalInputText = state.inputText;
|
|
408
|
+
newOriginalLongTextMap = state.longTextMap;
|
|
409
|
+
}
|
|
410
|
+
newIndex = Math.min(state.history.length - 1, newIndex + 1);
|
|
411
|
+
} else {
|
|
412
|
+
newIndex = Math.max(-1, newIndex - 1);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (newIndex === -1) {
|
|
416
|
+
return {
|
|
417
|
+
...state,
|
|
418
|
+
historyIndex: newIndex,
|
|
419
|
+
inputText: newOriginalInputText,
|
|
420
|
+
longTextMap: newOriginalLongTextMap,
|
|
421
|
+
cursorPosition: newOriginalInputText.length,
|
|
422
|
+
originalInputText: newOriginalInputText,
|
|
423
|
+
originalLongTextMap: newOriginalLongTextMap,
|
|
424
|
+
};
|
|
425
|
+
} else {
|
|
426
|
+
const entry = state.history[newIndex];
|
|
427
|
+
return {
|
|
428
|
+
...state,
|
|
429
|
+
historyIndex: newIndex,
|
|
430
|
+
inputText: entry.prompt,
|
|
431
|
+
longTextMap: entry.longTextMap || {},
|
|
432
|
+
cursorPosition: entry.prompt.length,
|
|
433
|
+
originalInputText: newOriginalInputText,
|
|
434
|
+
originalLongTextMap: newOriginalLongTextMap,
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
case "SELECT_HISTORY_ENTRY": {
|
|
439
|
+
const entry = action.payload;
|
|
440
|
+
return {
|
|
441
|
+
...state,
|
|
442
|
+
inputText: entry.prompt,
|
|
443
|
+
longTextMap: entry.longTextMap || {},
|
|
444
|
+
cursorPosition: entry.prompt.length,
|
|
445
|
+
historyIndex: -1,
|
|
446
|
+
history: [],
|
|
447
|
+
originalInputText: "",
|
|
448
|
+
originalLongTextMap: {},
|
|
449
|
+
showHistorySearch: false,
|
|
450
|
+
historySearchQuery: "",
|
|
451
|
+
selectorJustUsed: true,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
case "RESET_HISTORY_NAVIGATION":
|
|
455
|
+
return {
|
|
456
|
+
...state,
|
|
457
|
+
historyIndex: -1,
|
|
458
|
+
history: [],
|
|
459
|
+
originalInputText: "",
|
|
460
|
+
originalLongTextMap: {},
|
|
461
|
+
};
|
|
364
462
|
default:
|
|
365
463
|
return state;
|
|
366
464
|
}
|
package/src/utils/logger.ts
CHANGED
|
@@ -9,8 +9,11 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import * as fs from "fs";
|
|
12
|
+
import { Chalk } from "chalk";
|
|
12
13
|
import { LOG_FILE, DATA_DIRECTORY } from "./constants.js";
|
|
13
14
|
|
|
15
|
+
const chalk = new Chalk({ level: 3 });
|
|
16
|
+
|
|
14
17
|
const logFile = process.env.LOG_FILE || LOG_FILE;
|
|
15
18
|
|
|
16
19
|
/**
|
|
@@ -33,6 +36,16 @@ const LOG_LEVEL_NAMES = {
|
|
|
33
36
|
[LogLevel.ERROR]: "ERROR",
|
|
34
37
|
};
|
|
35
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Log level color mapping
|
|
41
|
+
*/
|
|
42
|
+
const LEVEL_COLORS = {
|
|
43
|
+
[LogLevel.DEBUG]: chalk.gray,
|
|
44
|
+
[LogLevel.INFO]: chalk.blue,
|
|
45
|
+
[LogLevel.WARN]: chalk.yellow,
|
|
46
|
+
[LogLevel.ERROR]: chalk.red,
|
|
47
|
+
};
|
|
48
|
+
|
|
36
49
|
/**
|
|
37
50
|
* Log configuration interface
|
|
38
51
|
*/
|
|
@@ -146,7 +159,8 @@ const logMessage = (level: LogLevel, ...args: unknown[]): void => {
|
|
|
146
159
|
|
|
147
160
|
const levelName = LOG_LEVEL_NAMES[level];
|
|
148
161
|
const timestamp = new Date().toISOString();
|
|
149
|
-
const
|
|
162
|
+
const color = LEVEL_COLORS[level] || ((s: string) => s);
|
|
163
|
+
const formattedMessage = `[${chalk.gray(timestamp)}] [${color(levelName)}] ${messageText}\n`;
|
|
150
164
|
|
|
151
165
|
try {
|
|
152
166
|
// Ensure directory exists
|