wave-code 0.0.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.
Files changed (131) hide show
  1. package/README.md +120 -0
  2. package/bin/wave-code.js +16 -0
  3. package/dist/cli.d.ts +6 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +62 -0
  6. package/dist/components/App.d.ts +8 -0
  7. package/dist/components/App.d.ts.map +1 -0
  8. package/dist/components/App.js +10 -0
  9. package/dist/components/BashHistorySelector.d.ts +10 -0
  10. package/dist/components/BashHistorySelector.d.ts.map +1 -0
  11. package/dist/components/BashHistorySelector.js +83 -0
  12. package/dist/components/BashShellManager.d.ts +6 -0
  13. package/dist/components/BashShellManager.d.ts.map +1 -0
  14. package/dist/components/BashShellManager.js +116 -0
  15. package/dist/components/ChatInterface.d.ts +3 -0
  16. package/dist/components/ChatInterface.d.ts.map +1 -0
  17. package/dist/components/ChatInterface.js +31 -0
  18. package/dist/components/CommandOutputDisplay.d.ts +9 -0
  19. package/dist/components/CommandOutputDisplay.d.ts.map +1 -0
  20. package/dist/components/CommandOutputDisplay.js +40 -0
  21. package/dist/components/CommandSelector.d.ts +11 -0
  22. package/dist/components/CommandSelector.d.ts.map +1 -0
  23. package/dist/components/CommandSelector.js +60 -0
  24. package/dist/components/CompressDisplay.d.ts +9 -0
  25. package/dist/components/CompressDisplay.d.ts.map +1 -0
  26. package/dist/components/CompressDisplay.js +17 -0
  27. package/dist/components/DiffViewer.d.ts +9 -0
  28. package/dist/components/DiffViewer.d.ts.map +1 -0
  29. package/dist/components/DiffViewer.js +221 -0
  30. package/dist/components/FileSelector.d.ts +13 -0
  31. package/dist/components/FileSelector.d.ts.map +1 -0
  32. package/dist/components/FileSelector.js +48 -0
  33. package/dist/components/InputBox.d.ts +23 -0
  34. package/dist/components/InputBox.d.ts.map +1 -0
  35. package/dist/components/InputBox.js +124 -0
  36. package/dist/components/McpManager.d.ts +10 -0
  37. package/dist/components/McpManager.d.ts.map +1 -0
  38. package/dist/components/McpManager.js +123 -0
  39. package/dist/components/MemoryDisplay.d.ts +8 -0
  40. package/dist/components/MemoryDisplay.d.ts.map +1 -0
  41. package/dist/components/MemoryDisplay.js +25 -0
  42. package/dist/components/MemoryTypeSelector.d.ts +8 -0
  43. package/dist/components/MemoryTypeSelector.d.ts.map +1 -0
  44. package/dist/components/MemoryTypeSelector.js +38 -0
  45. package/dist/components/MessageList.d.ts +12 -0
  46. package/dist/components/MessageList.d.ts.map +1 -0
  47. package/dist/components/MessageList.js +36 -0
  48. package/dist/components/ToolResultDisplay.d.ts +9 -0
  49. package/dist/components/ToolResultDisplay.d.ts.map +1 -0
  50. package/dist/components/ToolResultDisplay.js +52 -0
  51. package/dist/contexts/useAppConfig.d.ts +11 -0
  52. package/dist/contexts/useAppConfig.d.ts.map +1 -0
  53. package/dist/contexts/useAppConfig.js +13 -0
  54. package/dist/contexts/useChat.d.ts +36 -0
  55. package/dist/contexts/useChat.d.ts.map +1 -0
  56. package/dist/contexts/useChat.js +208 -0
  57. package/dist/hooks/useBashHistorySelector.d.ts +15 -0
  58. package/dist/hooks/useBashHistorySelector.d.ts.map +1 -0
  59. package/dist/hooks/useBashHistorySelector.js +61 -0
  60. package/dist/hooks/useCommandSelector.d.ts +24 -0
  61. package/dist/hooks/useCommandSelector.d.ts.map +1 -0
  62. package/dist/hooks/useCommandSelector.js +98 -0
  63. package/dist/hooks/useFileSelector.d.ts +16 -0
  64. package/dist/hooks/useFileSelector.d.ts.map +1 -0
  65. package/dist/hooks/useFileSelector.js +174 -0
  66. package/dist/hooks/useImageManager.d.ts +13 -0
  67. package/dist/hooks/useImageManager.d.ts.map +1 -0
  68. package/dist/hooks/useImageManager.js +46 -0
  69. package/dist/hooks/useInputHistory.d.ts +11 -0
  70. package/dist/hooks/useInputHistory.d.ts.map +1 -0
  71. package/dist/hooks/useInputHistory.js +64 -0
  72. package/dist/hooks/useInputKeyboardHandler.d.ts +83 -0
  73. package/dist/hooks/useInputKeyboardHandler.d.ts.map +1 -0
  74. package/dist/hooks/useInputKeyboardHandler.js +507 -0
  75. package/dist/hooks/useInputState.d.ts +14 -0
  76. package/dist/hooks/useInputState.d.ts.map +1 -0
  77. package/dist/hooks/useInputState.js +57 -0
  78. package/dist/hooks/useMemoryTypeSelector.d.ts +9 -0
  79. package/dist/hooks/useMemoryTypeSelector.d.ts.map +1 -0
  80. package/dist/hooks/useMemoryTypeSelector.js +27 -0
  81. package/dist/hooks/usePagination.d.ts +20 -0
  82. package/dist/hooks/usePagination.d.ts.map +1 -0
  83. package/dist/hooks/usePagination.js +168 -0
  84. package/dist/index.d.ts +5 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/index.js +91 -0
  87. package/dist/plain-cli.d.ts +7 -0
  88. package/dist/plain-cli.d.ts.map +1 -0
  89. package/dist/plain-cli.js +49 -0
  90. package/dist/utils/clipboard.d.ts +22 -0
  91. package/dist/utils/clipboard.d.ts.map +1 -0
  92. package/dist/utils/clipboard.js +347 -0
  93. package/dist/utils/constants.d.ts +17 -0
  94. package/dist/utils/constants.d.ts.map +1 -0
  95. package/dist/utils/constants.js +18 -0
  96. package/dist/utils/logger.d.ts +72 -0
  97. package/dist/utils/logger.d.ts.map +1 -0
  98. package/dist/utils/logger.js +245 -0
  99. package/package.json +60 -0
  100. package/src/cli.tsx +82 -0
  101. package/src/components/App.tsx +31 -0
  102. package/src/components/BashHistorySelector.tsx +163 -0
  103. package/src/components/BashShellManager.tsx +306 -0
  104. package/src/components/ChatInterface.tsx +88 -0
  105. package/src/components/CommandOutputDisplay.tsx +81 -0
  106. package/src/components/CommandSelector.tsx +144 -0
  107. package/src/components/CompressDisplay.tsx +58 -0
  108. package/src/components/DiffViewer.tsx +321 -0
  109. package/src/components/FileSelector.tsx +137 -0
  110. package/src/components/InputBox.tsx +310 -0
  111. package/src/components/McpManager.tsx +328 -0
  112. package/src/components/MemoryDisplay.tsx +62 -0
  113. package/src/components/MemoryTypeSelector.tsx +96 -0
  114. package/src/components/MessageList.tsx +215 -0
  115. package/src/components/ToolResultDisplay.tsx +138 -0
  116. package/src/contexts/useAppConfig.tsx +32 -0
  117. package/src/contexts/useChat.tsx +300 -0
  118. package/src/hooks/useBashHistorySelector.ts +77 -0
  119. package/src/hooks/useCommandSelector.ts +131 -0
  120. package/src/hooks/useFileSelector.ts +227 -0
  121. package/src/hooks/useImageManager.ts +64 -0
  122. package/src/hooks/useInputHistory.ts +74 -0
  123. package/src/hooks/useInputKeyboardHandler.ts +778 -0
  124. package/src/hooks/useInputState.ts +66 -0
  125. package/src/hooks/useMemoryTypeSelector.ts +40 -0
  126. package/src/hooks/usePagination.ts +203 -0
  127. package/src/index.ts +108 -0
  128. package/src/plain-cli.ts +66 -0
  129. package/src/utils/clipboard.ts +384 -0
  130. package/src/utils/constants.ts +22 -0
  131. package/src/utils/logger.ts +301 -0
@@ -0,0 +1,227 @@
1
+ import { useState, useCallback, useEffect, useRef } from "react";
2
+ import { glob } from "glob";
3
+ import { getGlobIgnorePatterns } from "wave-agent-sdk";
4
+ import * as fs from "fs";
5
+ import * as path from "path";
6
+ import { FileItem } from "../components/FileSelector.js";
7
+
8
+ export const useFileSelector = () => {
9
+ const [showFileSelector, setShowFileSelector] = useState(false);
10
+ const [atPosition, setAtPosition] = useState(-1);
11
+ const [searchQuery, setSearchQuery] = useState("");
12
+ const [filteredFiles, setFilteredFiles] = useState<FileItem[]>([]);
13
+ const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
14
+
15
+ // Check if path is a directory
16
+ const isDirectory = useCallback((filePath: string): boolean => {
17
+ try {
18
+ const fullPath = path.isAbsolute(filePath)
19
+ ? filePath
20
+ : path.join(process.cwd(), filePath);
21
+ return fs.statSync(fullPath).isDirectory();
22
+ } catch {
23
+ return false;
24
+ }
25
+ }, []);
26
+
27
+ // Convert string paths to FileItem objects
28
+ const convertToFileItems = useCallback(
29
+ (paths: string[]): FileItem[] => {
30
+ return paths.map((filePath) => ({
31
+ path: filePath,
32
+ type: isDirectory(filePath) ? "directory" : "file",
33
+ }));
34
+ },
35
+ [isDirectory],
36
+ );
37
+ // Use glob to search files and directories
38
+ const searchFiles = useCallback(
39
+ async (query: string) => {
40
+ try {
41
+ let files: string[] = [];
42
+ let directories: string[] = [];
43
+
44
+ const globOptions = {
45
+ ignore: getGlobIgnorePatterns(process.cwd()),
46
+ maxDepth: 10,
47
+ nocase: true, // Case insensitive
48
+ dot: true, // Include hidden files and directories
49
+ cwd: process.cwd(), // Specify search root directory
50
+ };
51
+
52
+ if (!query.trim()) {
53
+ // When query is empty, show some common file types and directories
54
+ const commonPatterns = [
55
+ "**/*.ts",
56
+ "**/*.tsx",
57
+ "**/*.js",
58
+ "**/*.jsx",
59
+ "**/*.json",
60
+ ];
61
+
62
+ // Search files
63
+ const filePromises = commonPatterns.map((pattern) =>
64
+ glob(pattern, { ...globOptions, nodir: true }),
65
+ );
66
+
67
+ // Search directories (only search first level to avoid too many results)
68
+ const dirPromises = [glob("*/", { ...globOptions, maxDepth: 1 })];
69
+
70
+ const fileResults = await Promise.all(filePromises);
71
+ const dirResults = await Promise.all(dirPromises);
72
+
73
+ files = fileResults.flat();
74
+ directories = dirResults.flat().map((dir) => {
75
+ // glob returns string type paths, remove trailing slash
76
+ return String(dir).replace(/\/$/, "");
77
+ });
78
+ } else {
79
+ // Build multiple glob patterns to support more flexible search
80
+ const filePatterns = [
81
+ // Match files with filenames containing query
82
+ `**/*${query}*`,
83
+ // Match files with query in path (match directory names)
84
+ `**/${query}*/**/*`,
85
+ ];
86
+
87
+ const dirPatterns = [
88
+ // Match directory names containing query
89
+ `**/*${query}*/`,
90
+ // Match directories containing query in path
91
+ `**/${query}*/`,
92
+ ];
93
+
94
+ // Search files
95
+ const filePromises = filePatterns.map((pattern) =>
96
+ glob(pattern, { ...globOptions, nodir: true }),
97
+ );
98
+
99
+ // Search directories
100
+ const dirPromises = dirPatterns.map((pattern) =>
101
+ glob(pattern, { ...globOptions, nodir: false }),
102
+ );
103
+
104
+ const fileResults = await Promise.all(filePromises);
105
+ const dirResults = await Promise.all(dirPromises);
106
+
107
+ files = fileResults.flat();
108
+ directories = dirResults.flat().map((dir) => {
109
+ // glob returns string type paths, remove trailing slash
110
+ return String(dir).replace(/\/$/, "");
111
+ });
112
+ }
113
+
114
+ // Deduplicate and merge files and directories
115
+ const uniqueFiles = Array.from(new Set(files));
116
+ const uniqueDirectories = Array.from(new Set(directories));
117
+ const allPaths = [...uniqueDirectories, ...uniqueFiles]; // Directories first
118
+
119
+ // Limit to maximum 10 results and convert to FileItem
120
+ const fileItems = convertToFileItems(allPaths.slice(0, 10));
121
+ setFilteredFiles(fileItems);
122
+ } catch (error) {
123
+ console.error("Glob search error:", error);
124
+ setFilteredFiles([]);
125
+ }
126
+ },
127
+ [convertToFileItems],
128
+ );
129
+
130
+ // Debounced search
131
+ const debouncedSearchFiles = useCallback(
132
+ (query: string) => {
133
+ // Clear previous timer
134
+ if (debounceTimerRef.current) {
135
+ clearTimeout(debounceTimerRef.current);
136
+ }
137
+
138
+ // Set new timer, support environment variable configuration
139
+ const debounceDelay = parseInt(
140
+ process.env.FILE_SELECTOR_DEBOUNCE_MS || "300",
141
+ 10,
142
+ );
143
+ debounceTimerRef.current = setTimeout(() => {
144
+ searchFiles(query);
145
+ }, debounceDelay);
146
+ },
147
+ [searchFiles],
148
+ );
149
+
150
+ // Trigger debounced search when search query changes
151
+ useEffect(() => {
152
+ debouncedSearchFiles(searchQuery);
153
+
154
+ // Cleanup function: clear timer when component unmounts
155
+ return () => {
156
+ if (debounceTimerRef.current) {
157
+ clearTimeout(debounceTimerRef.current);
158
+ }
159
+ };
160
+ }, [searchQuery, debouncedSearchFiles]);
161
+
162
+ const activateFileSelector = useCallback(
163
+ (position: number) => {
164
+ setShowFileSelector(true);
165
+ setAtPosition(position);
166
+ setSearchQuery("");
167
+ // Immediately trigger search to display initial file list, without waiting for debounce
168
+ searchFiles("");
169
+ },
170
+ [searchFiles],
171
+ );
172
+
173
+ const handleFileSelect = useCallback(
174
+ (filePath: string, inputText: string, cursorPosition: number) => {
175
+ if (atPosition >= 0) {
176
+ // Replace @ and search query with selected file path, remove @ symbol
177
+ const beforeAt = inputText.substring(0, atPosition);
178
+ const afterQuery = inputText.substring(cursorPosition);
179
+ const newInput = beforeAt + `${filePath} ` + afterQuery;
180
+ const newCursorPosition = beforeAt.length + filePath.length + 1;
181
+
182
+ setShowFileSelector(false);
183
+ setAtPosition(-1);
184
+ setSearchQuery("");
185
+ setFilteredFiles([]);
186
+
187
+ return { newInput, newCursorPosition };
188
+ }
189
+ return { newInput: inputText, newCursorPosition: cursorPosition };
190
+ },
191
+ [atPosition],
192
+ );
193
+
194
+ const handleCancelFileSelect = useCallback(() => {
195
+ setShowFileSelector(false);
196
+ setAtPosition(-1);
197
+ setSearchQuery("");
198
+ setFilteredFiles([]);
199
+ }, []);
200
+
201
+ const updateSearchQuery = useCallback((query: string) => {
202
+ setSearchQuery(query);
203
+ }, []);
204
+
205
+ const checkForAtDeletion = useCallback(
206
+ (cursorPosition: number) => {
207
+ if (showFileSelector && cursorPosition <= atPosition) {
208
+ handleCancelFileSelect();
209
+ return true;
210
+ }
211
+ return false;
212
+ },
213
+ [showFileSelector, atPosition, handleCancelFileSelect],
214
+ );
215
+
216
+ return {
217
+ showFileSelector,
218
+ filteredFiles,
219
+ searchQuery,
220
+ activateFileSelector,
221
+ handleFileSelect,
222
+ handleCancelFileSelect,
223
+ updateSearchQuery,
224
+ checkForAtDeletion,
225
+ atPosition,
226
+ };
227
+ };
@@ -0,0 +1,64 @@
1
+ import { useState, useCallback } from "react";
2
+ import { readClipboardImage } from "../utils/clipboard.js";
3
+
4
+ export interface AttachedImage {
5
+ id: number;
6
+ path: string;
7
+ mimeType: string;
8
+ }
9
+
10
+ export const useImageManager = (insertTextAtCursor: (text: string) => void) => {
11
+ const [attachedImages, setAttachedImages] = useState<AttachedImage[]>([]);
12
+ const [imageIdCounter, setImageIdCounter] = useState(1);
13
+
14
+ const addImage = useCallback(
15
+ (imagePath: string, mimeType: string) => {
16
+ const newImage: AttachedImage = {
17
+ id: imageIdCounter,
18
+ path: imagePath,
19
+ mimeType,
20
+ };
21
+ setAttachedImages((prev) => [...prev, newImage]);
22
+ setImageIdCounter((prev) => prev + 1);
23
+ return newImage;
24
+ },
25
+ [imageIdCounter],
26
+ );
27
+
28
+ const removeImage = useCallback((imageId: number) => {
29
+ setAttachedImages((prev) => prev.filter((img) => img.id !== imageId));
30
+ }, []);
31
+
32
+ const clearImages = useCallback(() => {
33
+ setAttachedImages([]);
34
+ }, []);
35
+
36
+ const handlePasteImage = useCallback(async () => {
37
+ try {
38
+ const result = await readClipboardImage();
39
+
40
+ if (result.success && result.imagePath && result.mimeType) {
41
+ // Add image to manager
42
+ const attachedImage = addImage(result.imagePath, result.mimeType);
43
+
44
+ // Insert image placeholder at cursor position
45
+ insertTextAtCursor(`[Image #${attachedImage.id}]`);
46
+
47
+ return true;
48
+ }
49
+
50
+ return false;
51
+ } catch (error) {
52
+ console.warn("Failed to paste image from clipboard:", error);
53
+ return false;
54
+ }
55
+ }, [addImage, insertTextAtCursor]);
56
+
57
+ return {
58
+ attachedImages,
59
+ addImage,
60
+ removeImage,
61
+ clearImages,
62
+ handlePasteImage,
63
+ };
64
+ };
@@ -0,0 +1,74 @@
1
+ import { useState, useCallback } from "react";
2
+
3
+ export interface UseInputHistoryParams {
4
+ userInputHistory: string[];
5
+ }
6
+
7
+ export const useInputHistory = ({
8
+ userInputHistory,
9
+ }: UseInputHistoryParams) => {
10
+ const [historyIndex, setHistoryIndex] = useState(-1);
11
+ const [currentDraftText, setCurrentDraftText] = useState("");
12
+
13
+ // Reset history navigation state
14
+ const resetHistoryNavigation = useCallback(() => {
15
+ setHistoryIndex(-1);
16
+ setCurrentDraftText("");
17
+ }, []);
18
+
19
+ // Handle history navigation
20
+ const navigateHistory = useCallback(
21
+ (direction: "up" | "down", inputText: string) => {
22
+ if (userInputHistory.length === 0)
23
+ return { newInput: inputText, newCursorPosition: inputText.length };
24
+
25
+ let newInput: string;
26
+
27
+ if (direction === "up") {
28
+ // Navigate up to earlier history records
29
+ if (historyIndex === -1) {
30
+ // First time pressing up key, save current input as draft
31
+ setCurrentDraftText(inputText);
32
+ const newIndex = userInputHistory.length - 1;
33
+ setHistoryIndex(newIndex);
34
+ newInput = userInputHistory[newIndex];
35
+ } else if (historyIndex > 0) {
36
+ const newIndex = historyIndex - 1;
37
+ setHistoryIndex(newIndex);
38
+ newInput = userInputHistory[newIndex];
39
+ } else {
40
+ newInput = inputText;
41
+ }
42
+ } else {
43
+ // Navigate down to newer history records
44
+ if (historyIndex >= 0) {
45
+ if (historyIndex < userInputHistory.length - 1) {
46
+ const newIndex = historyIndex + 1;
47
+ setHistoryIndex(newIndex);
48
+ newInput = userInputHistory[newIndex];
49
+ } else {
50
+ // Return to draft text
51
+ setHistoryIndex(-1);
52
+ newInput = currentDraftText;
53
+ }
54
+ } else {
55
+ // Already in draft state, pressing down key again clears input box
56
+ if (inputText.trim() !== "") {
57
+ setCurrentDraftText("");
58
+ newInput = "";
59
+ } else {
60
+ newInput = inputText;
61
+ }
62
+ }
63
+ }
64
+
65
+ return { newInput, newCursorPosition: newInput.length };
66
+ },
67
+ [userInputHistory, historyIndex, currentDraftText],
68
+ );
69
+
70
+ return {
71
+ resetHistoryNavigation,
72
+ navigateHistory,
73
+ };
74
+ };