vzcode 2.16.0 → 2.17.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.
package/dist/index.html CHANGED
@@ -20,8 +20,8 @@
20
20
  href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap"
21
21
  rel="stylesheet"
22
22
  />
23
- <script type="module" crossorigin src="/assets/index-C2BLPyQP.js"></script>
24
- <link rel="stylesheet" crossorigin href="/assets/index-DVR90LwF.css">
23
+ <script type="module" crossorigin src="/assets/index-BiPmUreW.js"></script>
24
+ <link rel="stylesheet" crossorigin href="/assets/index-BvGPtSrr.css">
25
25
  </head>
26
26
  <body>
27
27
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vzcode",
3
- "version": "2.16.0",
3
+ "version": "2.17.0",
4
4
  "description": "Multiplayer code editor system",
5
5
  "main": "src/index.ts",
6
6
  "type": "module",
@@ -3,11 +3,12 @@ import { Spinner } from '../../AIAssist/Spinner';
3
3
  interface AIEditingStatusIndicatorProps {
4
4
  status: string;
5
5
  fileName?: string;
6
+ additionalWidgets?: React.ReactNode;
6
7
  }
7
8
 
8
9
  export const AIEditingStatusIndicator: React.FC<
9
10
  AIEditingStatusIndicatorProps
10
- > = ({ status, fileName }) => {
11
+ > = ({ status, fileName, additionalWidgets }) => {
11
12
  const getStatusDisplay = (status: string) => {
12
13
  switch (status) {
13
14
  case 'Analyzing request...':
@@ -36,9 +37,16 @@ export const AIEditingStatusIndicator: React.FC<
36
37
  );
37
38
  case 'Done':
38
39
  return (
39
- <>
40
- <span>Done</span>
41
- </>
40
+ <div className="file-editing-done-container">
41
+ <div className="file-editing-done-status">
42
+ ✅ <span>Done</span>
43
+ </div>
44
+ {additionalWidgets && (
45
+ <div className="file-editing-done-widgets">
46
+ {additionalWidgets}
47
+ </div>
48
+ )}
49
+ </div>
42
50
  );
43
51
  default:
44
52
  // Handle file editing status (e.g., "Editing filename.js...")
@@ -1,31 +1,36 @@
1
- import { timestampToDate } from '@vizhub/viz-utils';
2
- import Markdown from 'react-markdown';
3
- import remarkGfm from 'remark-gfm';
4
1
  import React, {
5
2
  useMemo,
6
- memo,
7
3
  useContext,
8
4
  forwardRef,
9
5
  } from 'react';
6
+ import { timestampToDate } from '@vizhub/viz-utils';
7
+ import Markdown from 'react-markdown';
8
+ import remarkGfm from 'remark-gfm';
9
+ import { StreamingEvent } from '../../../types.js';
10
+ import { IndividualFileDiff } from './IndividualFileDiff';
11
+ import { VZCodeContext } from '../../VZCodeContext';
10
12
  import { DiffView, DiffViewRef } from './DiffView';
11
13
  import { UnifiedFilesDiff } from '../../../utils/fileDiff';
12
14
  import { enableDiffView } from '../../featureFlags';
13
- import { VZCodeContext } from '../../VZCodeContext';
15
+
16
+ const DEBUG = false;
14
17
 
15
18
  interface MessageProps {
16
19
  id: string;
17
20
  role: 'user' | 'assistant';
18
- content: string;
21
+ content?: string; // For non-streaming messages
19
22
  timestamp: number;
20
- isStreaming?: boolean;
21
- diffData?: UnifiedFilesDiff;
23
+ events?: StreamingEvent[]; // For streaming messages
24
+ isActive?: boolean; // Is this the currently streaming message?
22
25
  chatId?: string;
23
26
  showAdditionalWidgets?: boolean;
27
+ isStreaming?: boolean;
28
+ diffData?: UnifiedFilesDiff;
24
29
  }
25
30
 
26
- const MessageComponent = forwardRef<
31
+ export const Message = forwardRef<
27
32
  DiffViewRef,
28
- MessageProps
33
+ React.PropsWithChildren<MessageProps>
29
34
  >(
30
35
  (
31
36
  {
@@ -33,16 +38,25 @@ const MessageComponent = forwardRef<
33
38
  role,
34
39
  content,
35
40
  timestamp,
36
- isStreaming,
37
- diffData,
41
+ events = [],
42
+ isActive,
43
+ children,
38
44
  chatId,
39
45
  showAdditionalWidgets = false,
46
+ isStreaming = false,
47
+ diffData,
40
48
  },
41
49
  ref,
42
50
  ) => {
43
51
  const { additionalWidgets, handleSendMessage } =
44
52
  useContext(VZCodeContext);
45
53
 
54
+ DEBUG &&
55
+ console.log(
56
+ 'StreamingMessage: Rendered with events:',
57
+ events,
58
+ );
59
+
46
60
  // Memoize date formatting to avoid repeated computation
47
61
  const formattedTime = useMemo(() => {
48
62
  return timestampToDate(timestamp).toLocaleTimeString(
@@ -62,22 +76,63 @@ const MessageComponent = forwardRef<
62
76
  return (
63
77
  <div className={messageClassName}>
64
78
  <div className="ai-chat-message-content">
65
- <Markdown remarkPlugins={[remarkGfm]}>
66
- {content}
67
- </Markdown>
79
+ {/* Render regular content for non-streaming messages */}
80
+ {content && (
81
+ <Markdown remarkPlugins={[remarkGfm]}>
82
+ {content}
83
+ </Markdown>
84
+ )}
85
+
86
+ {/* Render diff view if present */}
68
87
  {enableDiffView &&
69
88
  diffData &&
70
89
  Object.keys(diffData).length > 0 && (
71
90
  <DiffView diffData={diffData} ref={ref} />
72
91
  )}
92
+
93
+ {/* Render events in order for streaming messages */}
94
+ {events.map((event, index) => {
95
+ switch (event.type) {
96
+ case 'text_chunk':
97
+ return (
98
+ <div
99
+ key={`text-${index}`}
100
+ className="text-chunk"
101
+ >
102
+ <Markdown remarkPlugins={[remarkGfm]}>
103
+ {event.content}
104
+ </Markdown>
105
+ </div>
106
+ );
107
+ case 'file_complete':
108
+ return (
109
+ <IndividualFileDiff
110
+ key={`file-${event.fileName}-${index}`}
111
+ fileName={event.fileName}
112
+ beforeContent={
113
+ event.beforeContent || ''
114
+ }
115
+ afterContent={event.afterContent || ''}
116
+ />
117
+ );
118
+ case 'file_start':
119
+ // File start events are now handled by centralized status logic in MessageList
120
+ return null;
121
+ default:
122
+ return null;
123
+ }
124
+ })}
125
+ {/* Additional widgets are now shown within the "Done" status indicator */}
73
126
  {showAdditionalWidgets &&
74
127
  additionalWidgets &&
75
128
  chatId &&
129
+ !isStreaming && // Only show here for non-streaming messages (completed messages)
76
130
  additionalWidgets({
77
131
  messageId: id,
78
132
  chatId: chatId,
79
133
  handleSendMessage,
80
134
  })}
135
+ {isActive && children}
81
136
  </div>
82
137
  <div className="ai-chat-message-time">
83
138
  {formattedTime}
@@ -87,6 +142,4 @@ const MessageComponent = forwardRef<
87
142
  },
88
143
  );
89
144
 
90
- MessageComponent.displayName = 'MessageComponent';
91
-
92
- export const Message = memo(MessageComponent);
145
+ Message.displayName = 'Message';
@@ -1,12 +1,18 @@
1
- import { useRef, useEffect, memo, useState } from 'react';
1
+ import {
2
+ useRef,
3
+ useEffect,
4
+ memo,
5
+ useState,
6
+ useContext,
7
+ } from 'react';
2
8
  import { Message } from './Message';
3
- import { StreamingMessage } from './StreamingMessage';
4
9
  import { TypingIndicator } from './TypingIndicator';
5
10
  import { ThinkingScratchpad } from './ThinkingScratchpad';
6
11
  import { AIEditingStatusIndicator } from './FileEditingIndicator';
7
12
  import { VizChatMessage } from '@vizhub/viz-types';
8
13
  import { ExtendedVizChatMessage } from '../../../types.js';
9
14
  import { DiffViewRef } from './DiffView.js';
15
+ import { VZCodeContext } from '../../VZCodeContext';
10
16
 
11
17
  const MessageListComponent = ({
12
18
  messages,
@@ -32,6 +38,10 @@ const MessageListComponent = ({
32
38
  const messagesEndRef = useRef<HTMLDivElement>(null);
33
39
  const diffViewRef = useRef<DiffViewRef>(null);
34
40
 
41
+ // Get additional widgets from context
42
+ const { additionalWidgets, handleSendMessage } =
43
+ useContext(VZCodeContext);
44
+
35
45
  // Track previous loading state to detect when AI generation completes
36
46
  const [prevIsLoading, setPrevIsLoading] =
37
47
  useState(isLoading);
@@ -162,54 +172,64 @@ const MessageListComponent = ({
162
172
  msg.role === 'assistant' &&
163
173
  index === lastAssistantMessageIndex;
164
174
 
165
- // Use StreamingMessage for assistant messages with streaming events
166
- if (
167
- msg.role === 'assistant' &&
168
- isStreamingMessage
169
- ) {
170
- return (
171
- <StreamingMessage
172
- key={msg.id}
173
- timestamp={msg.timestamp}
174
- events={extendedMsg.streamingEvents || []}
175
- isActive={index === lastAssistantMessageIndex}
176
- >
177
- {showThinkingScratchpad && (
178
- <ThinkingScratchpad
179
- content={aiScratchpad || ''}
180
- isVisible={showThinkingScratchpad}
181
- />
182
- )}
183
-
184
- {showConsolidatedStatusIndicator && (
185
- <AIEditingStatusIndicator
186
- status={consolidatedStatus?.status || ''}
187
- fileName={consolidatedStatus?.fileName}
188
- />
189
- )}
190
-
191
- {showTypingIndicator && <TypingIndicator />}
192
- </StreamingMessage>
193
- );
194
- }
195
-
196
- // Use regular Message component for user messages and non-streaming assistant messages
175
+ // Use Message for all messages now
197
176
  return (
198
177
  <Message
199
178
  key={msg.id}
200
179
  id={msg.id}
201
180
  role={msg.role}
202
- content={msg.content}
181
+ content={
182
+ isStreamingMessage ? undefined : msg.content
183
+ }
203
184
  timestamp={msg.timestamp}
204
- diffData={(msg as any).diffData}
185
+ events={
186
+ isStreamingMessage
187
+ ? extendedMsg.streamingEvents || []
188
+ : []
189
+ }
190
+ isActive={index === lastAssistantMessageIndex}
205
191
  chatId={chatId}
206
192
  showAdditionalWidgets={showAdditionalWidgets}
193
+ isStreaming={isStreamingMessage}
194
+ diffData={(msg as any).diffData}
207
195
  ref={
208
196
  isLastMessage && (msg as any).diffData
209
197
  ? diffViewRef
210
198
  : null
211
199
  }
212
- />
200
+ >
201
+ {isStreamingMessage &&
202
+ showThinkingScratchpad && (
203
+ <ThinkingScratchpad
204
+ content={aiScratchpad || ''}
205
+ isVisible={showThinkingScratchpad}
206
+ />
207
+ )}
208
+
209
+ {isStreamingMessage &&
210
+ showConsolidatedStatusIndicator && (
211
+ <AIEditingStatusIndicator
212
+ status={consolidatedStatus?.status || ''}
213
+ fileName={consolidatedStatus?.fileName}
214
+ additionalWidgets={
215
+ consolidatedStatus?.status === 'Done' &&
216
+ showAdditionalWidgets &&
217
+ additionalWidgets &&
218
+ chatId
219
+ ? additionalWidgets({
220
+ messageId: msg.id,
221
+ chatId: chatId,
222
+ handleSendMessage,
223
+ })
224
+ : undefined
225
+ }
226
+ />
227
+ )}
228
+
229
+ {isStreamingMessage && showTypingIndicator && (
230
+ <TypingIndicator />
231
+ )}
232
+ </Message>
213
233
  );
214
234
  })}
215
235
 
@@ -78,9 +78,6 @@ export const AIChat = () => {
78
78
  afterRender,
79
79
  } = useAutoScroll({ threshold: 24 });
80
80
 
81
- // TODO fix this - currently always false
82
- console.log('showJumpButton:', showJumpButton);
83
-
84
81
  // Get the active chat ID and chat data
85
82
  // If selectedChatId is set, use it; otherwise, if no chat selected, don't default to currentChatId
86
83
  const activeChatId = selectedChatId;
@@ -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