vzcode 2.18.0 → 2.22.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/README.md +359 -0
- package/dist/assets/index-CfDs-dAu.js +476 -0
- package/dist/assets/{index-BvGPtSrr.css → index-fSroLVgi.css} +1 -1
- package/dist/assets/{worker-y5jhqCTR.js → worker-Cm3I3tnR.js} +57 -57
- package/dist/assets/{worker-ClF0pBYr.js → worker-KiuLM-X4.js} +71 -71
- package/dist/index.html +2 -2
- package/dist/llm-streaming-server/aiEditing.js +58 -0
- package/dist/llm-streaming-server/chatOperations.js +494 -0
- package/dist/llm-streaming-server/errorHandling.js +49 -0
- package/dist/llm-streaming-server/index.js +20 -0
- package/dist/llm-streaming-server/llmStreaming.js +265 -0
- package/dist/llm-streaming-server/validation.js +19 -0
- package/dist/server/aiChatHandler/chatOperations.js +3 -4
- package/dist/server/aiChatHandler/index.js +5 -5
- package/dist/server/aiChatHandler/llmStreaming.js +57 -4
- package/dist/server/prettier.js +18 -16
- package/dist/utils/fileDiff.js +25 -1
- package/package.json +45 -45
- package/src/client/CodeEditor/getOrCreateEditor.tsx +272 -258
- package/src/client/CodeEditor/index.tsx +3 -3
- package/src/client/VZRight.tsx +4 -0
- package/src/client/VZSidebar/AIChat/ChatInput.tsx +1 -1
- package/src/client/VZSidebar/AIChat/DiffView.scss +37 -1
- package/src/client/VZSidebar/AIChat/DiffView.tsx +55 -16
- package/src/client/VZSidebar/AIChat/styles.scss +38 -0
- package/src/client/VZSidebar/FileTypeIcon.tsx +1 -0
- package/src/client/VZSidebar/index.tsx +1 -1
- package/src/client/featureFlags.ts +7 -0
- package/src/llm-streaming-server/README.md +71 -0
- package/src/llm-streaming-server/aiEditing.ts +102 -0
- package/src/llm-streaming-server/chatOperations.ts +655 -0
- package/src/llm-streaming-server/errorHandling.ts +66 -0
- package/src/llm-streaming-server/index.ts +51 -0
- package/src/llm-streaming-server/llmStreaming.ts +403 -0
- package/src/llm-streaming-server/validation.ts +24 -0
- package/src/llm-streaming-ui/README.md +103 -0
- package/src/llm-streaming-ui/components/ChatInput.tsx +237 -0
- package/src/llm-streaming-ui/components/DiffView.scss +173 -0
- package/src/llm-streaming-ui/components/DiffView.tsx +236 -0
- package/src/llm-streaming-ui/components/FileEditingIndicator.tsx +89 -0
- package/src/llm-streaming-ui/components/IndividualFileDiff.tsx +86 -0
- package/src/llm-streaming-ui/components/JumpToLatestButton.tsx +64 -0
- package/src/llm-streaming-ui/components/Message.tsx +145 -0
- package/src/llm-streaming-ui/components/MessageList.tsx +241 -0
- package/src/llm-streaming-ui/components/ThinkingScratchpad.tsx +42 -0
- package/src/llm-streaming-ui/components/TypingIndicator.tsx +19 -0
- package/src/llm-streaming-ui/components/index.tsx +388 -0
- package/src/llm-streaming-ui/components/styles.scss +831 -0
- package/src/llm-streaming-ui/components/useSpeechRecognition.ts +116 -0
- package/src/llm-streaming-ui/index.ts +34 -0
- package/src/server/aiChatHandler/chatOperations.ts +3 -4
- package/src/server/aiChatHandler/index.ts +5 -5
- package/src/server/aiChatHandler/llmStreaming.ts +87 -4
- package/src/server/prettier.ts +30 -31
- package/src/types.ts +5 -0
- package/src/utils/fileDiff.ts +36 -2
- package/dist/assets/index-DuDXdJWC.js +0 -477
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useContext,
|
|
3
|
+
useMemo,
|
|
4
|
+
useEffect,
|
|
5
|
+
useCallback,
|
|
6
|
+
} from 'react';
|
|
7
|
+
import { VZCodeContext } from '../../client/VZCodeContext';
|
|
8
|
+
import { MessageList } from './MessageList';
|
|
9
|
+
import { ChatInput } from './ChatInput';
|
|
10
|
+
import { ExtendedVizChat } from '../../types.js';
|
|
11
|
+
import { useAutoScroll } from '../../client/hooks/useAutoScroll';
|
|
12
|
+
import { JumpToLatestButton } from './JumpToLatestButton';
|
|
13
|
+
import './styles.scss';
|
|
14
|
+
|
|
15
|
+
const DEBUG = false;
|
|
16
|
+
|
|
17
|
+
const showSuggestedRequests = false;
|
|
18
|
+
|
|
19
|
+
const showPreviousChats = false;
|
|
20
|
+
|
|
21
|
+
// Component for displaying the list of existing chats
|
|
22
|
+
const ChatList = ({
|
|
23
|
+
chats,
|
|
24
|
+
selectedChatId,
|
|
25
|
+
onSelectChat,
|
|
26
|
+
getChatTitle,
|
|
27
|
+
}) => {
|
|
28
|
+
return (
|
|
29
|
+
<div className="ai-chat-list">
|
|
30
|
+
<div className="ai-chat-list-header">
|
|
31
|
+
<h4>Previous Chats</h4>
|
|
32
|
+
</div>
|
|
33
|
+
<div className="ai-chat-suggested-prompts">
|
|
34
|
+
{chats.map((chat) => (
|
|
35
|
+
<button
|
|
36
|
+
key={chat.id}
|
|
37
|
+
className={`ai-chat-suggested-prompt ${
|
|
38
|
+
selectedChatId === chat.id ? 'selected' : ''
|
|
39
|
+
}`}
|
|
40
|
+
onClick={() => onSelectChat(chat.id)}
|
|
41
|
+
>
|
|
42
|
+
{getChatTitle(chat)}
|
|
43
|
+
</button>
|
|
44
|
+
))}
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const AIChat = () => {
|
|
51
|
+
const {
|
|
52
|
+
aiChatFocused,
|
|
53
|
+
content,
|
|
54
|
+
aiChatMode,
|
|
55
|
+
aiChatMessage,
|
|
56
|
+
currentChatId,
|
|
57
|
+
selectedChatId,
|
|
58
|
+
setSelectedChatId,
|
|
59
|
+
aiErrorMessage,
|
|
60
|
+
setAIChatMode,
|
|
61
|
+
getStoredAIPrompt,
|
|
62
|
+
setAIChatMessage,
|
|
63
|
+
handleSendMessage,
|
|
64
|
+
setAIErrorMessage,
|
|
65
|
+
navigateMessageHistoryUp,
|
|
66
|
+
navigateMessageHistoryDown,
|
|
67
|
+
resetMessageHistoryNavigation,
|
|
68
|
+
enableMinimalEditFlow,
|
|
69
|
+
} = useContext(VZCodeContext);
|
|
70
|
+
|
|
71
|
+
// Use the new simplified auto-scroll hook
|
|
72
|
+
const {
|
|
73
|
+
containerRef: messagesContainerRef,
|
|
74
|
+
showJumpButton,
|
|
75
|
+
onNewEvent,
|
|
76
|
+
onJumpToLatest,
|
|
77
|
+
beforeRender,
|
|
78
|
+
afterRender,
|
|
79
|
+
} = useAutoScroll({ threshold: 24 });
|
|
80
|
+
|
|
81
|
+
// Get the active chat ID and chat data
|
|
82
|
+
// If selectedChatId is set, use it; otherwise, if no chat selected, don't default to currentChatId
|
|
83
|
+
const activeChatId = selectedChatId;
|
|
84
|
+
const currentChat = activeChatId
|
|
85
|
+
? content?.chats?.[activeChatId]
|
|
86
|
+
: null;
|
|
87
|
+
const rawMessages = useMemo(
|
|
88
|
+
() => currentChat?.messages || [],
|
|
89
|
+
[currentChat?.messages],
|
|
90
|
+
);
|
|
91
|
+
const aiStatus = currentChat?.aiStatus;
|
|
92
|
+
const aiScratchpad = currentChat?.aiScratchpad;
|
|
93
|
+
const currentStatus = (currentChat as ExtendedVizChat)
|
|
94
|
+
?.currentStatus;
|
|
95
|
+
|
|
96
|
+
// Debug logging for AI status
|
|
97
|
+
DEBUG && console.log('AIChat: currentChat:', currentChat);
|
|
98
|
+
DEBUG && console.log('AIChat: aiStatus:', aiStatus);
|
|
99
|
+
DEBUG &&
|
|
100
|
+
console.log(
|
|
101
|
+
'AIChat: enableMinimalEditFlow:',
|
|
102
|
+
enableMinimalEditFlow,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// Transform messages to ensure they have required id field - memoized to avoid recreation
|
|
106
|
+
const messages = useMemo(
|
|
107
|
+
() =>
|
|
108
|
+
rawMessages.map((msg, index) => ({
|
|
109
|
+
...msg,
|
|
110
|
+
id: msg.id || `msg-${index}`,
|
|
111
|
+
})),
|
|
112
|
+
[rawMessages],
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// Check if this is the first time opening the chat (no messages) or no chat selected
|
|
116
|
+
const isEmptyState =
|
|
117
|
+
!selectedChatId || rawMessages.length === 0;
|
|
118
|
+
|
|
119
|
+
// Get all existing chats
|
|
120
|
+
const allChats = content?.chats || {};
|
|
121
|
+
const existingChats = Object.values(allChats).filter(
|
|
122
|
+
(chat) => chat.messages.length > 0,
|
|
123
|
+
);
|
|
124
|
+
const hasExistingChats = existingChats.length > 0;
|
|
125
|
+
|
|
126
|
+
// Generate title for a chat (first 50 characters of first user message)
|
|
127
|
+
const getChatTitle = (chat) => {
|
|
128
|
+
const firstUserMessage = chat.messages.find(
|
|
129
|
+
(msg) => msg.role === 'user',
|
|
130
|
+
);
|
|
131
|
+
if (!firstUserMessage) return 'New Chat';
|
|
132
|
+
return (
|
|
133
|
+
firstUserMessage.content.slice(0, 50) +
|
|
134
|
+
(firstUserMessage.content.length > 50 ? '...' : '')
|
|
135
|
+
);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Wrapper for handleSendMessage to automatically select the chat when sending
|
|
139
|
+
const handleSendMessageWithSelection = useCallback(() => {
|
|
140
|
+
// If no chat is selected, select the currentChatId when sending a message
|
|
141
|
+
if (!selectedChatId) {
|
|
142
|
+
setSelectedChatId(currentChatId);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Call the original handleSendMessage without parameters
|
|
146
|
+
return handleSendMessage();
|
|
147
|
+
}, [
|
|
148
|
+
selectedChatId,
|
|
149
|
+
setSelectedChatId,
|
|
150
|
+
currentChatId,
|
|
151
|
+
handleSendMessage,
|
|
152
|
+
]);
|
|
153
|
+
|
|
154
|
+
// Check for stored AI prompt on component mount (post-fork restoration)
|
|
155
|
+
// This logic is now handled by the AutoSendAIMessage component in VizHub
|
|
156
|
+
// to ensure proper timing and coordination with editor opening
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
DEBUG &&
|
|
159
|
+
console.log('AIChat: Checking for stored AI prompt');
|
|
160
|
+
if (getStoredAIPrompt) {
|
|
161
|
+
const storedPrompt = getStoredAIPrompt();
|
|
162
|
+
DEBUG &&
|
|
163
|
+
console.log(
|
|
164
|
+
'AIChat: Stored prompt result:',
|
|
165
|
+
storedPrompt,
|
|
166
|
+
);
|
|
167
|
+
if (storedPrompt) {
|
|
168
|
+
// Only restore the prompt and mode, but don't auto-send
|
|
169
|
+
// The AutoSendAIMessage component will handle the sending
|
|
170
|
+
DEBUG &&
|
|
171
|
+
console.log(
|
|
172
|
+
'AIChat: Restoring prompt and mode only',
|
|
173
|
+
);
|
|
174
|
+
setAIChatMessage(storedPrompt.prompt);
|
|
175
|
+
setAIChatMode(
|
|
176
|
+
storedPrompt.modelName === 'ask' ? 'ask' : 'edit',
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// Don't clear the stored prompt here - let AutoSendAIMessage handle it
|
|
180
|
+
// Don't auto-submit here - let AutoSendAIMessage handle the timing
|
|
181
|
+
DEBUG &&
|
|
182
|
+
console.log(
|
|
183
|
+
'AIChat: Prompt restored, waiting for AutoSendAIMessage to handle sending',
|
|
184
|
+
);
|
|
185
|
+
} else {
|
|
186
|
+
DEBUG &&
|
|
187
|
+
console.log('AIChat: No stored prompt found');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}, [getStoredAIPrompt, setAIChatMessage, setAIChatMode]);
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<div className="ai-chat-container">
|
|
194
|
+
<div className="ai-chat-content">
|
|
195
|
+
<div
|
|
196
|
+
className="ai-chat-messages-container"
|
|
197
|
+
ref={messagesContainerRef}
|
|
198
|
+
>
|
|
199
|
+
{isEmptyState ? (
|
|
200
|
+
<div className="ai-chat-empty">
|
|
201
|
+
<div className="ai-chat-empty-icon">✨</div>
|
|
202
|
+
<h3 className="ai-chat-empty-title">
|
|
203
|
+
Edit with AI
|
|
204
|
+
</h3>
|
|
205
|
+
<div className="ai-chat-empty-text">
|
|
206
|
+
How can I help you?
|
|
207
|
+
</div>
|
|
208
|
+
{hasExistingChats && showPreviousChats && (
|
|
209
|
+
<ChatList
|
|
210
|
+
chats={existingChats}
|
|
211
|
+
selectedChatId={selectedChatId}
|
|
212
|
+
onSelectChat={setSelectedChatId}
|
|
213
|
+
getChatTitle={getChatTitle}
|
|
214
|
+
/>
|
|
215
|
+
)}
|
|
216
|
+
{showSuggestedRequests && (
|
|
217
|
+
<div className="ai-chat-empty-examples">
|
|
218
|
+
{aiChatMode === 'ask' ? (
|
|
219
|
+
<>
|
|
220
|
+
<h4>Try asking questions like:</h4>
|
|
221
|
+
<div className="ai-chat-suggested-prompts">
|
|
222
|
+
<button
|
|
223
|
+
className="ai-chat-suggested-prompt"
|
|
224
|
+
onClick={() =>
|
|
225
|
+
handleSendMessage(
|
|
226
|
+
'Explain how this works',
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
>
|
|
230
|
+
"Explain how this works"
|
|
231
|
+
</button>
|
|
232
|
+
<button
|
|
233
|
+
className="ai-chat-suggested-prompt"
|
|
234
|
+
onClick={() =>
|
|
235
|
+
handleSendMessage(
|
|
236
|
+
'How could I change it so that the circles are bigger?',
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
>
|
|
240
|
+
"How could I change it so
|
|
241
|
+
that the circles are bigger?"
|
|
242
|
+
</button>
|
|
243
|
+
<button
|
|
244
|
+
className="ai-chat-suggested-prompt"
|
|
245
|
+
onClick={() =>
|
|
246
|
+
handleSendMessage(
|
|
247
|
+
'What does this function do?',
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
>
|
|
251
|
+
"What does this function
|
|
252
|
+
do?"
|
|
253
|
+
</button>
|
|
254
|
+
<button
|
|
255
|
+
className="ai-chat-suggested-prompt"
|
|
256
|
+
onClick={() =>
|
|
257
|
+
handleSendMessage(
|
|
258
|
+
'How can I make this more accessible?',
|
|
259
|
+
)
|
|
260
|
+
}
|
|
261
|
+
>
|
|
262
|
+
"How can I make this more
|
|
263
|
+
accessible?"
|
|
264
|
+
</button>
|
|
265
|
+
</div>
|
|
266
|
+
</>
|
|
267
|
+
) : (
|
|
268
|
+
<>
|
|
269
|
+
<h4>Try edit requests like these:</h4>
|
|
270
|
+
<div className="ai-chat-suggested-prompts">
|
|
271
|
+
<button
|
|
272
|
+
className="ai-chat-suggested-prompt"
|
|
273
|
+
onClick={() =>
|
|
274
|
+
handleSendMessage(
|
|
275
|
+
'Change the circles to squares',
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
>
|
|
279
|
+
"Change the circles to
|
|
280
|
+
squares"
|
|
281
|
+
</button>
|
|
282
|
+
<button
|
|
283
|
+
className="ai-chat-suggested-prompt"
|
|
284
|
+
onClick={() =>
|
|
285
|
+
handleSendMessage(
|
|
286
|
+
'Add a button that toggles the animation',
|
|
287
|
+
)
|
|
288
|
+
}
|
|
289
|
+
>
|
|
290
|
+
"Add a button that toggles
|
|
291
|
+
the animation"
|
|
292
|
+
</button>
|
|
293
|
+
<button
|
|
294
|
+
className="ai-chat-suggested-prompt"
|
|
295
|
+
onClick={() =>
|
|
296
|
+
handleSendMessage(
|
|
297
|
+
'Fix the CSS so the layout is responsive',
|
|
298
|
+
)
|
|
299
|
+
}
|
|
300
|
+
>
|
|
301
|
+
"Fix the CSS so the layout is
|
|
302
|
+
responsive"
|
|
303
|
+
</button>
|
|
304
|
+
<button
|
|
305
|
+
className="ai-chat-suggested-prompt"
|
|
306
|
+
onClick={() =>
|
|
307
|
+
handleSendMessage(
|
|
308
|
+
'Refactor this function to use async/await',
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
>
|
|
312
|
+
"Refactor this function to
|
|
313
|
+
use async/await"
|
|
314
|
+
</button>
|
|
315
|
+
</div>
|
|
316
|
+
</>
|
|
317
|
+
)}
|
|
318
|
+
</div>
|
|
319
|
+
)}
|
|
320
|
+
{showSuggestedRequests && (
|
|
321
|
+
<div className="ai-chat-empty-text">
|
|
322
|
+
{aiChatMode === 'ask'
|
|
323
|
+
? 'Type your question below to get started!'
|
|
324
|
+
: 'Type your edit request below to get started!'}
|
|
325
|
+
</div>
|
|
326
|
+
)}
|
|
327
|
+
</div>
|
|
328
|
+
) : (
|
|
329
|
+
<MessageList
|
|
330
|
+
messages={messages}
|
|
331
|
+
isLoading={false}
|
|
332
|
+
chatId={selectedChatId || currentChatId}
|
|
333
|
+
aiScratchpad={aiScratchpad}
|
|
334
|
+
currentStatus={currentStatus}
|
|
335
|
+
onNewEvent={onNewEvent}
|
|
336
|
+
onJumpToLatest={onJumpToLatest}
|
|
337
|
+
beforeRender={beforeRender}
|
|
338
|
+
afterRender={afterRender}
|
|
339
|
+
/>
|
|
340
|
+
)}
|
|
341
|
+
{aiErrorMessage && (
|
|
342
|
+
<div className="ai-chat-error">
|
|
343
|
+
<div className="ai-chat-error-content">
|
|
344
|
+
<span className="ai-chat-error-icon">
|
|
345
|
+
⚠️
|
|
346
|
+
</span>
|
|
347
|
+
<span className="ai-chat-error-message">
|
|
348
|
+
{aiErrorMessage}
|
|
349
|
+
</span>
|
|
350
|
+
<button
|
|
351
|
+
className="ai-chat-error-dismiss"
|
|
352
|
+
onClick={() => setAIErrorMessage(null)}
|
|
353
|
+
aria-label="Dismiss error"
|
|
354
|
+
>
|
|
355
|
+
×
|
|
356
|
+
</button>
|
|
357
|
+
</div>
|
|
358
|
+
</div>
|
|
359
|
+
)}
|
|
360
|
+
{/* Jump to Latest Button */}
|
|
361
|
+
<JumpToLatestButton
|
|
362
|
+
visible={showJumpButton}
|
|
363
|
+
onClick={onJumpToLatest}
|
|
364
|
+
/>
|
|
365
|
+
</div>
|
|
366
|
+
</div>
|
|
367
|
+
<div className="ai-chat-input-fixed">
|
|
368
|
+
<ChatInput
|
|
369
|
+
aiChatMessage={aiChatMessage}
|
|
370
|
+
setAIChatMessage={setAIChatMessage}
|
|
371
|
+
onSendMessage={handleSendMessageWithSelection}
|
|
372
|
+
focused={aiChatFocused}
|
|
373
|
+
aiChatMode={aiChatMode}
|
|
374
|
+
setAIChatMode={setAIChatMode}
|
|
375
|
+
navigateMessageHistoryUp={
|
|
376
|
+
navigateMessageHistoryUp
|
|
377
|
+
}
|
|
378
|
+
navigateMessageHistoryDown={
|
|
379
|
+
navigateMessageHistoryDown
|
|
380
|
+
}
|
|
381
|
+
resetMessageHistoryNavigation={
|
|
382
|
+
resetMessageHistoryNavigation
|
|
383
|
+
}
|
|
384
|
+
/>
|
|
385
|
+
</div>
|
|
386
|
+
</div>
|
|
387
|
+
);
|
|
388
|
+
};
|