vzcode 2.16.0 → 2.18.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.
@@ -140,8 +140,7 @@
140
140
  .file-editing-indicator {
141
141
  display: flex;
142
142
  align-items: center;
143
- padding: 0.75rem;
144
- margin: 0.5rem 0;
143
+ margin: 0.25rem 0;
145
144
  background-color: var(--vscode-editor-background);
146
145
  border: 1px solid var(--vscode-panel-border);
147
146
  border-radius: 4px;
@@ -150,6 +149,7 @@
150
149
  display: flex;
151
150
  align-items: center;
152
151
  gap: 0.5rem;
152
+ width: 100%;
153
153
  }
154
154
 
155
155
  .file-editing-icon {
@@ -161,6 +161,7 @@
161
161
  .file-editing-text {
162
162
  color: var(--vscode-foreground);
163
163
  font-size: 0.9rem;
164
+ flex: 1;
164
165
 
165
166
  code {
166
167
  background-color: var(
@@ -170,6 +171,36 @@
170
171
  border-radius: 3px;
171
172
  font-family: var(--vscode-editor-font-family);
172
173
  }
174
+
175
+ .file-editing-done-container {
176
+ display: flex;
177
+ align-items: center;
178
+ justify-content: space-between;
179
+ width: 100%;
180
+ gap: 0.5rem;
181
+ }
182
+
183
+ .file-editing-done-status {
184
+ display: flex;
185
+ align-items: center;
186
+ gap: 8px;
187
+ font-size: 16px;
188
+ font-weight: 500;
189
+ margin-left: 4px;
190
+ }
191
+
192
+ .file-editing-done-widgets {
193
+ display: flex;
194
+ align-items: center;
195
+ gap: 0.5rem;
196
+ flex-shrink: 0;
197
+
198
+ // // Override the margin-top for undo-button-container when inside file editing indicator
199
+ // .undo-button-container {
200
+ // margin-top: 0;
201
+ // margin-bottom: 0;
202
+ // }
203
+ }
173
204
  }
174
205
  }
175
206
 
@@ -288,33 +319,6 @@
288
319
  line-height: 1.4;
289
320
  word-wrap: break-word;
290
321
 
291
- .undo-button-container {
292
- display: flex;
293
- justify-content: flex-end;
294
- gap: 8px;
295
- margin-top: 16px;
296
- }
297
-
298
- .undo-button {
299
- background-color: #dc3545;
300
- color: white;
301
- border: none;
302
- border-radius: 4px;
303
- padding: 4px 8px;
304
- font-size: 12px;
305
- cursor: pointer;
306
- transition: background-color 0.2s;
307
-
308
- &:hover:not(:disabled) {
309
- background-color: #c82333;
310
- }
311
-
312
- &:disabled {
313
- background-color: #6c757d;
314
- cursor: not-allowed;
315
- }
316
- }
317
-
318
322
  // Markdown styling for dark backgrounds
319
323
  pre {
320
324
  background-color: rgba(0, 0, 0, 0.3);
@@ -148,7 +148,6 @@ export const useAutoScroll = (
148
148
  // Ignore scroll events during programmatic scrolling
149
149
  if (isProgrammaticScrollRef.current) return;
150
150
 
151
- console.log('handleScroll called');
152
151
  const container = containerRef.current;
153
152
  if (!container) return;
154
153
 
@@ -7,9 +7,6 @@ import {
7
7
  createFilesSnapshot,
8
8
  generateFilesUnifiedDiff,
9
9
  } from '../../utils/fileDiff.js';
10
- import { formatFiles } from '../prettier.js';
11
- import { createSubmitOperation } from '../../submitOperation.js';
12
- import { VizContent } from '@vizhub/viz-types';
13
10
 
14
11
  // Dev flag for waiting 1 second before starting the LLM function.
15
12
  // Useful for debugging and testing purposes, e.g. checking the typing indicator.
@@ -60,7 +57,7 @@ export const performAIEditing = async ({
60
57
  llmFunction,
61
58
  runCode,
62
59
  }) => {
63
- // 1. Capture the current state of files before editing
60
+ // Capture the current state of files before editing
64
61
  const beforeFiles = createFilesSnapshot(
65
62
  shareDBDoc.data.files,
66
63
  );
@@ -70,7 +67,7 @@ export const performAIEditing = async ({
70
67
  );
71
68
  const filesContext = formatMarkdownFiles(files);
72
69
 
73
- // 2. Assemble the final prompt
70
+ // Assemble the final prompt
74
71
  const fullPrompt = assembleFullPrompt({
75
72
  filesContext,
76
73
  prompt,
@@ -83,55 +80,9 @@ export const performAIEditing = async ({
83
80
  );
84
81
  }
85
82
 
86
- // Call the LLM function which will handle streaming and incremental file updates
83
+ // Call the LLM function which will handle streaming, incremental file updates, and Prettier formatting
87
84
  const result = await llmFunction(fullPrompt);
88
85
 
89
- // 3. Run Prettier on changed files before running code
90
- const afterLLMFiles = createFilesSnapshot(
91
- shareDBDoc.data.files,
92
- );
93
-
94
- // Find files that were changed by the AI
95
- const changedFileIds = Object.keys(afterLLMFiles).filter(
96
- (fileId) =>
97
- beforeFiles[fileId]?.text !==
98
- afterLLMFiles[fileId]?.text,
99
- );
100
-
101
- if (changedFileIds.length > 0) {
102
- // Format the changed files
103
- const formattedFiles = await formatFiles(
104
- shareDBDoc.data.files,
105
- changedFileIds,
106
- );
107
-
108
- // Apply formatted versions to shareDBDoc if formatting succeeded
109
- if (Object.keys(formattedFiles).length > 0) {
110
- const submitOperation =
111
- createSubmitOperation(shareDBDoc);
112
- submitOperation((document: VizContent) => {
113
- const updatedFiles = { ...document.files };
114
-
115
- // Apply each formatted file
116
- Object.entries(formattedFiles).forEach(
117
- ([fileId, formattedText]) => {
118
- if (updatedFiles[fileId]) {
119
- updatedFiles[fileId] = {
120
- ...updatedFiles[fileId],
121
- text: formattedText,
122
- };
123
- }
124
- },
125
- );
126
-
127
- return {
128
- ...document,
129
- files: updatedFiles,
130
- };
131
- });
132
- }
133
- }
134
-
135
86
  runCode();
136
87
 
137
88
  // 4. Capture the state of files after editing and formatting, then generate diff
@@ -376,6 +376,8 @@ export const updateFiles = (
376
376
  ...shareDBDoc.data,
377
377
  files,
378
378
  });
379
+ DEBUG && console.log('updateFiles op:');
380
+ DEBUG && console.log(JSON.stringify(filesOp, null, 2));
379
381
  shareDBDoc.submitOp(filesOp);
380
382
  };
381
383
 
@@ -4,8 +4,10 @@ import {
4
4
  StreamingMarkdownParser,
5
5
  } from 'llm-code-format';
6
6
  import { mergeFileChanges } from 'editcodewithai';
7
- import { VizChatId } from '@vizhub/viz-types';
8
- import { generateRunId } from '@vizhub/viz-utils';
7
+ import {
8
+ FileCollection,
9
+ VizChatId,
10
+ } from '@vizhub/viz-types';
9
11
  import {
10
12
  updateFiles,
11
13
  updateAIScratchpad,
@@ -14,11 +16,11 @@ import {
14
16
  updateStreamingStatus,
15
17
  finalizeStreamingMessage,
16
18
  } from './chatOperations.js';
17
- import { diff } from '../../ot.js';
18
19
  import {
19
20
  ShareDBDoc,
20
21
  ExtendedVizContent,
21
22
  } from '../../types.js';
23
+ import { formatFiles } from '../prettier.js';
22
24
 
23
25
  const DEBUG = false;
24
26
 
@@ -285,51 +287,30 @@ export const createLLMFunction = ({
285
287
  await completeFileEditing(currentEditingFileName);
286
288
  }
287
289
 
290
+ // // Capture the current state of files before applying changes
291
+ // const beforeFiles = createFilesSnapshot(
292
+ // shareDBDoc.data.files,
293
+ // );
294
+
295
+ // Parse the full content to extract file changes
296
+ // export type FileCollection = Record<string, string>;
297
+ const newFilesUnformatted: FileCollection =
298
+ parseMarkdownFiles(fullContent, 'bold').files;
299
+
300
+ // Run Prettier on `newFiles` before applying them
301
+ const newFilesFormatted = await formatFiles(
302
+ newFilesUnformatted,
303
+ );
304
+
288
305
  // Apply all the edits at once
289
- updateFiles(
290
- shareDBDoc,
291
- mergeFileChanges(
292
- shareDBDoc.data.files,
293
- parseMarkdownFiles(fullContent, 'bold').files,
294
- ),
306
+ const mergedChanges = mergeFileChanges(
307
+ shareDBDoc.data.files,
308
+ newFilesFormatted,
295
309
  );
310
+ updateFiles(shareDBDoc, mergedChanges);
296
311
 
297
312
  // Finalize streaming message
298
- await finalizeStreamingMessage(shareDBDoc, chatId);
299
-
300
- // Generate a new runId to trigger a run when AI finishes editing
301
- // This will trigger a re-run without hot reloading
302
- const newRunId = generateRunId();
303
- const runIdOp = diff(shareDBDoc.data, {
304
- ...shareDBDoc.data,
305
- runId: newRunId,
306
- });
307
- shareDBDoc.submitOp(runIdOp, (error) => {
308
- if (error) {
309
- console.warn(
310
- 'Error setting runId after AI editing:',
311
- error,
312
- );
313
- } else {
314
- DEBUG &&
315
- console.log(
316
- 'Set new runId after AI editing:',
317
- newRunId,
318
- );
319
- }
320
- });
321
-
322
- // Write chunks file for debugging
323
- // if (DEBUG) {
324
- // const chunksFileJSONpath = `./ai-chunks-${chatId}.json`;
325
- // fs.writeFileSync(
326
- // chunksFileJSONpath,
327
- // JSON.stringify(chunks, null, 2),
328
- // );
329
- // console.log(
330
- // `AI chunks written to ${chunksFileJSONpath}`,
331
- // );
332
- // }
313
+ finalizeStreamingMessage(shareDBDoc, chatId);
333
314
 
334
315
  return {
335
316
  content: fullContent,
@@ -5,7 +5,10 @@ import * as prettierPluginHtml from 'prettier/plugins/html';
5
5
  import * as prettierPluginMarkdown from 'prettier/plugins/markdown';
6
6
  import * as prettierPluginCSS from 'prettier/plugins/postcss';
7
7
  import * as prettierPluginTypescript from 'prettier/plugins/typescript';
8
- import { VizFiles, VizFileId } from '@vizhub/viz-types';
8
+ import {
9
+ VizFileId,
10
+ FileCollection,
11
+ } from '@vizhub/viz-types';
9
12
 
10
13
  // Parser mappings - matches client-side implementation
11
14
  const parsers = {
@@ -88,23 +91,36 @@ export const formatFile = async (
88
91
  * Returns a map of fileId -> formatted text for successfully formatted files
89
92
  */
90
93
  export const formatFiles = async (
91
- files: VizFiles,
92
- fileIds?: VizFileId[],
94
+ fileCollection:
95
+ | FileCollection
96
+ | { [key: string]: { name: string; text: string } },
93
97
  ): Promise<{ [fileId: VizFileId]: string }> => {
94
98
  const results: { [fileId: VizFileId]: string } = {};
95
- const targetFileIds = fileIds || Object.keys(files);
99
+ const targetFileIds = Object.keys(fileCollection);
96
100
 
97
101
  // Process files in parallel for better performance
98
102
  const formatPromises = targetFileIds.map(
99
103
  async (fileId) => {
100
- const file = files[fileId];
101
- if (!file) return;
104
+ const fileData = fileCollection[fileId];
105
+ if (!fileData) return;
106
+
107
+ // Handle both FileCollection format and test format
108
+ const fileName =
109
+ typeof fileData === 'string'
110
+ ? fileId
111
+ : fileData.name;
112
+ const fileText =
113
+ typeof fileData === 'string'
114
+ ? fileData
115
+ : fileData.text;
116
+
117
+ if (!fileText) return;
102
118
 
103
119
  const formatted = await formatFile(
104
- file.text,
105
- file.name,
120
+ fileText,
121
+ fileName,
106
122
  );
107
- if (formatted !== null && formatted !== file.text) {
123
+ if (formatted !== null && formatted !== fileText) {
108
124
  results[fileId] = formatted;
109
125
  }
110
126
  },
@@ -1,77 +0,0 @@
1
- import React, { useMemo } from 'react';
2
- import { timestampToDate } from '@vizhub/viz-utils';
3
- import Markdown from 'react-markdown';
4
- import remarkGfm from 'remark-gfm';
5
- import { StreamingEvent } from '../../../types.js';
6
- import { IndividualFileDiff } from './IndividualFileDiff';
7
-
8
- const DEBUG = false;
9
-
10
- interface StreamingMessageProps {
11
- timestamp: number;
12
- events: StreamingEvent[];
13
- isActive?: boolean; // Is this the currently streaming message?
14
- children?: React.ReactNode;
15
- }
16
-
17
- export const StreamingMessage: React.FC<
18
- StreamingMessageProps
19
- > = ({ timestamp, events, isActive, children }) => {
20
- DEBUG &&
21
- console.log(
22
- 'StreamingMessage: Rendered with events:',
23
- events,
24
- );
25
-
26
- // Memoize date formatting to avoid repeated computation
27
- const formattedTime = useMemo(() => {
28
- return timestampToDate(timestamp).toLocaleTimeString(
29
- [],
30
- {
31
- hour: '2-digit',
32
- minute: '2-digit',
33
- },
34
- );
35
- }, [timestamp]);
36
-
37
- return (
38
- <div className="ai-chat-message assistant streaming">
39
- <div className="ai-chat-message-content">
40
- {/* Render events in order */}
41
- {events.map((event, index) => {
42
- switch (event.type) {
43
- case 'text_chunk':
44
- return (
45
- <div
46
- key={`text-${index}`}
47
- className="text-chunk"
48
- >
49
- <Markdown remarkPlugins={[remarkGfm]}>
50
- {event.content}
51
- </Markdown>
52
- </div>
53
- );
54
- case 'file_complete':
55
- return (
56
- <IndividualFileDiff
57
- key={`file-${event.fileName}-${index}`}
58
- fileName={event.fileName}
59
- beforeContent={event.beforeContent || ''}
60
- afterContent={event.afterContent || ''}
61
- />
62
- );
63
- case 'file_start':
64
- // File start events are now handled by centralized status logic in MessageList
65
- return null;
66
- default:
67
- return null;
68
- }
69
- })}
70
- {isActive && children}
71
- </div>
72
- <div className="ai-chat-message-time">
73
- {formattedTime}
74
- </div>
75
- </div>
76
- );
77
- };