vzcode 2.14.0 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/assets/bindings_wasm_bg-9w8E-TmY.wasm +0 -0
  2. package/dist/assets/buildWorker-Bi6wsfQk.js +695 -0
  3. package/dist/assets/{index-AX9wEDr7.js → index-C2BLPyQP.js} +138 -125
  4. package/dist/assets/{index-DLm5FQ0E.css → index-DVR90LwF.css} +1 -1
  5. package/dist/assets/worker-CmHJK_do.js +136 -0
  6. package/dist/index.html +2 -2
  7. package/dist/server/aiChatHandler/chatOperations.js +168 -35
  8. package/dist/server/aiChatHandler/index.js +11 -30
  9. package/dist/server/aiChatHandler/llmStreaming.js +105 -118
  10. package/package.json +20 -20
  11. package/src/client/AIAssist/AIAssistWidget/index.tsx +12 -1
  12. package/src/client/App/useShareDB.ts +1 -1
  13. package/src/client/CodeEditor/index.tsx +9 -10
  14. package/src/client/VZCodeContext/types.ts +3 -1
  15. package/src/client/VZCodeContext/useVZCodeState.ts +7 -5
  16. package/src/client/VZRight.tsx +10 -2
  17. package/src/client/VZSidebar/AIChat/ChatInput.tsx +1 -7
  18. package/src/client/VZSidebar/AIChat/DiffView.scss +1 -0
  19. package/src/client/VZSidebar/AIChat/DiffView.tsx +72 -29
  20. package/src/client/VZSidebar/AIChat/FileEditingIndicator.tsx +81 -0
  21. package/src/client/VZSidebar/AIChat/IndividualFileDiff.tsx +86 -0
  22. package/src/client/VZSidebar/AIChat/JumpToLatestButton.tsx +64 -0
  23. package/src/client/VZSidebar/AIChat/Message.tsx +68 -53
  24. package/src/client/VZSidebar/AIChat/MessageList.tsx +139 -118
  25. package/src/client/VZSidebar/AIChat/StreamingMessage.tsx +63 -21
  26. package/src/client/VZSidebar/AIChat/ThinkingScratchpad.tsx +4 -118
  27. package/src/client/VZSidebar/AIChat/index.tsx +62 -19
  28. package/src/client/VZSidebar/AIChat/styles.scss +159 -16
  29. package/src/client/VZSidebar/Item.tsx +2 -2
  30. package/src/client/VZSidebar/Search.tsx +13 -6
  31. package/src/client/VZSidebar/VisualEditor/VisualEditor.tsx +39 -23
  32. package/src/client/VZSidebar/VisualEditor/utils.ts +1 -1
  33. package/src/client/VZSidebar/index.tsx +12 -10
  34. package/src/client/VZSidebar/useDragAndDrop.tsx +225 -214
  35. package/src/client/featureFlags.ts +3 -0
  36. package/src/client/hooks/useAutoScroll.ts +329 -0
  37. package/src/client/tabsSearchParameters.ts +1 -17
  38. package/src/client/useFileCRUD.ts +5 -2
  39. package/src/client/useKeyboardShortcuts.ts +7 -0
  40. package/src/client/useOpenDirectories.ts +2 -1
  41. package/src/client/usePrettier/index.ts +1 -1
  42. package/src/client/useURLSync.ts +1 -0
  43. package/src/client/utils/scrollUtils.ts +170 -0
  44. package/src/client/vzReducer/searchReducer.ts +6 -3
  45. package/src/server/aiChatHandler/chatOperations.ts +224 -43
  46. package/src/server/aiChatHandler/index.ts +8 -48
  47. package/src/server/aiChatHandler/llmStreaming.ts +149 -170
  48. package/src/types.ts +81 -1
  49. package/dist/assets/bindings_wasm_bg-CH5ekNIA.wasm +0 -0
  50. package/dist/assets/buildWorker-D4WATavZ.js +0 -682
  51. package/dist/assets/worker-CHvi_bXc.js +0 -136
@@ -18,6 +18,44 @@ interface FileSystemDirectoryReader {
18
18
 
19
19
  const DEBUG = false;
20
20
 
21
+ const isTextFile = (file: File): boolean => {
22
+ // Add more text file types as needed
23
+ const textTypes = [
24
+ 'text/',
25
+ 'application/json',
26
+ 'application/javascript',
27
+ 'application/typescript',
28
+ 'application/xml',
29
+ 'application/x-httpd-php',
30
+ ];
31
+ const isText =
32
+ textTypes.some((type) => file.type.startsWith(type)) ||
33
+ file.name.match(
34
+ /\.(txt|js|jsx|ts|tsx|md|css|scss|html|xml|json|php|py|rb|java|c|cpp|h|hpp)$/i,
35
+ ) !== null;
36
+
37
+ DEBUG &&
38
+ console.log(
39
+ `[useDragAndDrop] File ${file.name} is${isText ? '' : ' not'} a text file`,
40
+ );
41
+ return isText;
42
+ };
43
+
44
+ const isImageFile = (file: File): boolean => {
45
+ const imageTypes = ['image/'];
46
+ const isImage =
47
+ imageTypes.some((type) => file.type.startsWith(type)) ||
48
+ file.name.match(
49
+ /\.(png|jpg|jpeg|gif|bmp|svg|webp)$/i,
50
+ ) !== null;
51
+
52
+ DEBUG &&
53
+ console.log(
54
+ `[useDragAndDrop] File ${file.name} is${isImage ? '' : ' not'} an image file`,
55
+ );
56
+ return isImage;
57
+ };
58
+
21
59
  export const useDragAndDrop = () => {
22
60
  const [isDragOver, setIsDragOver] = useState(false);
23
61
  const [isProcessing, setIsProcessing] = useState(false);
@@ -53,269 +91,237 @@ export const useDragAndDrop = () => {
53
91
  [],
54
92
  );
55
93
 
56
- const isTextFile = (file: File): boolean => {
57
- // Add more text file types as needed
58
- const textTypes = [
59
- 'text/',
60
- 'application/json',
61
- 'application/javascript',
62
- 'application/typescript',
63
- 'application/xml',
64
- 'application/x-httpd-php',
65
- ];
66
- const isText =
67
- textTypes.some((type) =>
68
- file.type.startsWith(type),
69
- ) ||
70
- file.name.match(
71
- /\.(txt|js|jsx|ts|tsx|md|css|scss|html|xml|json|php|py|rb|java|c|cpp|h|hpp)$/i,
72
- ) !== null;
73
-
74
- DEBUG &&
75
- console.log(
76
- `[useDragAndDrop] File ${file.name} is${isText ? '' : ' not'} a text file`,
77
- );
78
- return isText;
79
- };
80
-
81
- const isImageFile = (file: File): boolean => {
82
- const imageTypes = ['image/'];
83
- const isImage =
84
- imageTypes.some((type) =>
85
- file.type.startsWith(type),
86
- ) ||
87
- file.name.match(
88
- /\.(png|jpg|jpeg|gif|bmp|svg|webp)$/i,
89
- ) !== null;
90
-
91
- DEBUG &&
92
- console.log(
93
- `[useDragAndDrop] File ${file.name} is${isImage ? '' : ' not'} an image file`,
94
- );
95
- return isImage;
96
- };
97
-
98
- const readFileAsText = (file: File): Promise<string> => {
99
- return new Promise((resolve, reject) => {
100
- DEBUG &&
101
- console.log(
102
- `[useDragAndDrop] Reading file: ${file.name}`,
103
- );
104
-
105
- if (!isTextFile(file)) {
94
+ const readFileAsText = useCallback(
95
+ (file: File): Promise<string> => {
96
+ return new Promise((resolve, reject) => {
106
97
  DEBUG &&
107
98
  console.log(
108
- `[useDragAndDrop] Rejected non-text file: ${file.name}`,
99
+ `[useDragAndDrop] Reading file: ${file.name}`,
109
100
  );
110
- reject(
111
- new Error(`File ${file.name} is not a text file`),
112
- );
113
- return;
114
- }
115
101
 
116
- const reader = new FileReader();
117
- reader.onload = (e) => {
118
- if (e.target?.result) {
102
+ if (!isTextFile(file)) {
119
103
  DEBUG &&
120
104
  console.log(
121
- `[useDragAndDrop] Successfully read file: ${file.name}`,
122
- );
123
- resolve(e.target.result as string);
124
- } else {
125
- DEBUG &&
126
- console.log(
127
- `[useDragAndDrop] Failed to read file: ${file.name}`,
105
+ `[useDragAndDrop] Rejected non-text file: ${file.name}`,
128
106
  );
129
107
  reject(
130
- new Error(`Failed to read file ${file.name}`),
108
+ new Error(
109
+ `File ${file.name} is not a text file`,
110
+ ),
131
111
  );
112
+ return;
132
113
  }
133
- };
134
- reader.onerror = () => {
135
- DEBUG &&
136
- console.log(
137
- `[useDragAndDrop] Error reading file: ${file.name}`,
138
- );
139
- reject(
140
- new Error(`Error reading file ${file.name}`),
141
- );
142
- };
143
- reader.readAsText(file);
144
- });
145
- };
146
-
147
- const readImageAsBase64 = (
148
- file: File,
149
- ): Promise<string> => {
150
- return new Promise((resolve, reject) => {
151
- DEBUG &&
152
- console.log(
153
- `[useDragAndDrop] Reading image file: ${file.name}`,
154
- );
155
-
156
- if (!isImageFile(file)) {
157
- DEBUG &&
158
- console.log(
159
- `[useDragAndDrop] Rejected non-image file: ${file.name}`,
160
- );
161
- reject(
162
- new Error(
163
- `File ${file.name} is not an image file`,
164
- ),
165
- );
166
- return;
167
- }
168
114
 
169
- // For SVG files, read as text if they're text-based
170
- if (
171
- file.type === 'image/svg+xml' ||
172
- file.name.toLowerCase().endsWith('.svg')
173
- ) {
174
- const textReader = new FileReader();
175
- textReader.onload = (e) => {
115
+ const reader = new FileReader();
116
+ reader.onload = (e) => {
176
117
  if (e.target?.result) {
177
118
  DEBUG &&
178
119
  console.log(
179
- `[useDragAndDrop] Successfully read SVG file as text: ${file.name}`,
120
+ `[useDragAndDrop] Successfully read file: ${file.name}`,
180
121
  );
181
122
  resolve(e.target.result as string);
182
123
  } else {
124
+ DEBUG &&
125
+ console.log(
126
+ `[useDragAndDrop] Failed to read file: ${file.name}`,
127
+ );
183
128
  reject(
184
- new Error(
185
- `Failed to read SVG file ${file.name}`,
186
- ),
129
+ new Error(`Failed to read file ${file.name}`),
187
130
  );
188
131
  }
189
132
  };
190
- textReader.onerror = () =>
191
- reject(
192
- new Error(
193
- `Error reading SVG file ${file.name}`,
194
- ),
195
- );
196
- textReader.readAsText(file);
197
- return;
198
- }
199
-
200
- const reader = new FileReader();
201
- reader.onload = (e) => {
202
- if (e.target?.result) {
133
+ reader.onerror = () => {
203
134
  DEBUG &&
204
135
  console.log(
205
- `[useDragAndDrop] Successfully read image file: ${file.name}`,
136
+ `[useDragAndDrop] Error reading file: ${file.name}`,
206
137
  );
207
- // Return just the base64 part without the data URL prefix
208
- const base64 = (e.target.result as string).split(
209
- ',',
210
- )[1];
211
- resolve(base64);
212
- } else {
138
+ reject(
139
+ new Error(`Error reading file ${file.name}`),
140
+ );
141
+ };
142
+ reader.readAsText(file);
143
+ });
144
+ },
145
+ [],
146
+ );
147
+
148
+ const readImageAsBase64 = useCallback(
149
+ (file: File): Promise<string> => {
150
+ return new Promise((resolve, reject) => {
151
+ DEBUG &&
152
+ console.log(
153
+ `[useDragAndDrop] Reading image file: ${file.name}`,
154
+ );
155
+
156
+ if (!isImageFile(file)) {
213
157
  DEBUG &&
214
158
  console.log(
215
- `[useDragAndDrop] Failed to read image file: ${file.name}`,
159
+ `[useDragAndDrop] Rejected non-image file: ${file.name}`,
216
160
  );
217
161
  reject(
218
162
  new Error(
219
- `Failed to read image file ${file.name}`,
163
+ `File ${file.name} is not an image file`,
220
164
  ),
221
165
  );
166
+ return;
222
167
  }
223
- };
224
- reader.onerror = () => {
225
- DEBUG &&
226
- console.log(
227
- `[useDragAndDrop] Error reading image file: ${file.name}`,
228
- );
229
- reject(
230
- new Error(
231
- `Error reading image file ${file.name}`,
232
- ),
233
- );
234
- };
235
- reader.readAsDataURL(file);
236
- });
237
- };
238
-
239
- const processEntry = async (
240
- entry: FileSystemEntry,
241
- path: string,
242
- ): Promise<void> => {
243
- try {
244
- DEBUG &&
245
- console.log(
246
- `[useDragAndDrop] Processing entry: ${entry.name} at path: ${path}`,
247
- );
248
168
 
249
- if (entry.isFile) {
250
- DEBUG &&
251
- console.log(
252
- `[useDragAndDrop] Processing file: ${entry.name}`,
253
- );
254
- entry.file(async (file) => {
255
- try {
256
- if (isImageFile(file)) {
257
- const content = await readImageAsBase64(file);
169
+ // For SVG files, read as text if they're text-based
170
+ if (
171
+ file.type === 'image/svg+xml' ||
172
+ file.name.toLowerCase().endsWith('.svg')
173
+ ) {
174
+ const textReader = new FileReader();
175
+ textReader.onload = (e) => {
176
+ if (e.target?.result) {
258
177
  DEBUG &&
259
178
  console.log(
260
- `[useDragAndDrop] Creating image file: ${path}${file.name}`,
179
+ `[useDragAndDrop] Successfully read SVG file as text: ${file.name}`,
261
180
  );
262
- createFile(`${path}${file.name}`, content);
181
+ resolve(e.target.result as string);
263
182
  } else {
264
- const content = await readFileAsText(file);
265
- DEBUG &&
266
- console.log(
267
- `[useDragAndDrop] Creating text file: ${path}${file.name}`,
268
- );
269
- createFile(`${path}${file.name}`, content);
183
+ reject(
184
+ new Error(
185
+ `Failed to read SVG file ${file.name}`,
186
+ ),
187
+ );
270
188
  }
271
- } catch (error) {
272
- console.error(
273
- `Error processing file ${file.name}:`,
274
- error,
189
+ };
190
+ textReader.onerror = () =>
191
+ reject(
192
+ new Error(
193
+ `Error reading SVG file ${file.name}`,
194
+ ),
195
+ );
196
+ textReader.readAsText(file);
197
+ return;
198
+ }
199
+
200
+ const reader = new FileReader();
201
+ reader.onload = (e) => {
202
+ if (e.target?.result) {
203
+ DEBUG &&
204
+ console.log(
205
+ `[useDragAndDrop] Successfully read image file: ${file.name}`,
206
+ );
207
+ // Return just the base64 part without the data URL prefix
208
+ const base64 = (
209
+ e.target.result as string
210
+ ).split(',')[1];
211
+ resolve(base64);
212
+ } else {
213
+ DEBUG &&
214
+ console.log(
215
+ `[useDragAndDrop] Failed to read image file: ${file.name}`,
216
+ );
217
+ reject(
218
+ new Error(
219
+ `Failed to read image file ${file.name}`,
220
+ ),
275
221
  );
276
222
  }
277
- });
278
- } else if (entry.isDirectory) {
223
+ };
224
+ reader.onerror = () => {
225
+ DEBUG &&
226
+ console.log(
227
+ `[useDragAndDrop] Error reading image file: ${file.name}`,
228
+ );
229
+ reject(
230
+ new Error(
231
+ `Error reading image file ${file.name}`,
232
+ ),
233
+ );
234
+ };
235
+ reader.readAsDataURL(file);
236
+ });
237
+ },
238
+ [],
239
+ );
240
+
241
+ const processEntry = useCallback(
242
+ async (
243
+ entry: FileSystemEntry,
244
+ path: string,
245
+ ): Promise<void> => {
246
+ try {
279
247
  DEBUG &&
280
248
  console.log(
281
- `[useDragAndDrop] Processing directory: ${entry.name}`,
249
+ `[useDragAndDrop] Processing entry: ${entry.name} at path: ${path}`,
282
250
  );
283
- return new Promise<void>((resolve) => {
284
- const dirReader = entry.createReader();
285
- const readEntries = () => {
286
- dirReader.readEntries(async (entries) => {
287
- if (entries.length === 0) {
251
+
252
+ if (entry.isFile) {
253
+ DEBUG &&
254
+ console.log(
255
+ `[useDragAndDrop] Processing file: ${entry.name}`,
256
+ );
257
+ entry.file(async (file) => {
258
+ try {
259
+ if (isImageFile(file)) {
260
+ const content =
261
+ await readImageAsBase64(file);
288
262
  DEBUG &&
289
263
  console.log(
290
- `[useDragAndDrop] Finished reading directory: ${entry.name}`,
264
+ `[useDragAndDrop] Creating image file: ${path}${file.name}`,
291
265
  );
292
- resolve();
293
- return;
266
+ createFile(`${path}${file.name}`, content);
267
+ } else {
268
+ const content = await readFileAsText(file);
269
+ DEBUG &&
270
+ console.log(
271
+ `[useDragAndDrop] Creating text file: ${path}${file.name}`,
272
+ );
273
+ createFile(`${path}${file.name}`, content);
294
274
  }
275
+ } catch (error) {
276
+ console.error(
277
+ `Error processing file ${file.name}:`,
278
+ error,
279
+ );
280
+ }
281
+ });
282
+ } else if (entry.isDirectory) {
283
+ DEBUG &&
284
+ console.log(
285
+ `[useDragAndDrop] Processing directory: ${entry.name}`,
286
+ );
287
+ return new Promise<void>((resolve) => {
288
+ const dirReader = entry.createReader();
289
+ const readEntries = () => {
290
+ dirReader.readEntries(async (entries) => {
291
+ if (entries.length === 0) {
292
+ DEBUG &&
293
+ console.log(
294
+ `[useDragAndDrop] Finished reading directory: ${entry.name}`,
295
+ );
296
+ resolve();
297
+ return;
298
+ }
295
299
 
296
- const newPath = `${path}${entry.name}/`;
297
- DEBUG &&
298
- console.log(
299
- `[useDragAndDrop] Reading ${entries.length} entries in directory: ${newPath}`,
300
+ const newPath = `${path}${entry.name}/`;
301
+ DEBUG &&
302
+ console.log(
303
+ `[useDragAndDrop] Reading ${entries.length} entries in directory: ${newPath}`,
304
+ );
305
+ await Promise.all(
306
+ entries.map((entry) =>
307
+ processEntry(entry, newPath),
308
+ ),
300
309
  );
301
- await Promise.all(
302
- entries.map((entry) =>
303
- processEntry(entry, newPath),
304
- ),
305
- );
306
- readEntries(); // Continue reading if there are more entries
307
- });
308
- };
309
- readEntries();
310
- });
310
+ readEntries(); // Continue reading if there are more entries
311
+ });
312
+ };
313
+ readEntries();
314
+ });
315
+ }
316
+ } catch (error) {
317
+ console.error(
318
+ `Error processing entry ${entry.name}:`,
319
+ error,
320
+ );
311
321
  }
312
- } catch (error) {
313
- console.error(
314
- `Error processing entry ${entry.name}:`,
315
- error,
316
- );
317
- }
318
- };
322
+ },
323
+ [createFile, readFileAsText, readImageAsBase64],
324
+ );
319
325
 
320
326
  const handleDrop = useCallback(
321
327
  async (event: React.DragEvent<HTMLDivElement>) => {
@@ -394,7 +400,12 @@ export const useDragAndDrop = () => {
394
400
  setIsProcessing(false);
395
401
  }
396
402
  },
397
- [createFile],
403
+ [
404
+ createFile,
405
+ processEntry,
406
+ readFileAsText,
407
+ readImageAsBase64,
408
+ ],
398
409
  );
399
410
 
400
411
  return {
@@ -6,3 +6,6 @@ export const enableAIChat = true;
6
6
  export const enableDiffView = true;
7
7
 
8
8
  export const enableAskMode = false;
9
+
10
+ // Phase 0: Feature flag for minimal AI edit flow
11
+ export const enableMinimalEditFlow = true;