vzcode 2.15.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 (45) hide show
  1. package/dist/assets/{index-BpUpNto4.js → index-C2BLPyQP.js} +138 -125
  2. package/dist/assets/{index-DLm5FQ0E.css → index-DVR90LwF.css} +1 -1
  3. package/dist/index.html +2 -2
  4. package/dist/server/aiChatHandler/chatOperations.js +168 -35
  5. package/dist/server/aiChatHandler/index.js +11 -30
  6. package/dist/server/aiChatHandler/llmStreaming.js +105 -118
  7. package/package.json +11 -11
  8. package/src/client/AIAssist/AIAssistWidget/index.tsx +12 -1
  9. package/src/client/App/useShareDB.ts +1 -1
  10. package/src/client/CodeEditor/index.tsx +9 -10
  11. package/src/client/VZCodeContext/types.ts +3 -1
  12. package/src/client/VZCodeContext/useVZCodeState.ts +7 -5
  13. package/src/client/VZRight.tsx +10 -2
  14. package/src/client/VZSidebar/AIChat/ChatInput.tsx +1 -7
  15. package/src/client/VZSidebar/AIChat/DiffView.scss +1 -0
  16. package/src/client/VZSidebar/AIChat/DiffView.tsx +72 -29
  17. package/src/client/VZSidebar/AIChat/FileEditingIndicator.tsx +81 -0
  18. package/src/client/VZSidebar/AIChat/IndividualFileDiff.tsx +86 -0
  19. package/src/client/VZSidebar/AIChat/JumpToLatestButton.tsx +64 -0
  20. package/src/client/VZSidebar/AIChat/Message.tsx +68 -53
  21. package/src/client/VZSidebar/AIChat/MessageList.tsx +139 -118
  22. package/src/client/VZSidebar/AIChat/StreamingMessage.tsx +63 -21
  23. package/src/client/VZSidebar/AIChat/ThinkingScratchpad.tsx +4 -118
  24. package/src/client/VZSidebar/AIChat/index.tsx +62 -19
  25. package/src/client/VZSidebar/AIChat/styles.scss +159 -16
  26. package/src/client/VZSidebar/Item.tsx +2 -2
  27. package/src/client/VZSidebar/Search.tsx +13 -6
  28. package/src/client/VZSidebar/VisualEditor/VisualEditor.tsx +39 -23
  29. package/src/client/VZSidebar/VisualEditor/utils.ts +1 -1
  30. package/src/client/VZSidebar/index.tsx +12 -10
  31. package/src/client/VZSidebar/useDragAndDrop.tsx +225 -214
  32. package/src/client/featureFlags.ts +3 -0
  33. package/src/client/hooks/useAutoScroll.ts +329 -0
  34. package/src/client/tabsSearchParameters.ts +1 -17
  35. package/src/client/useFileCRUD.ts +5 -2
  36. package/src/client/useKeyboardShortcuts.ts +7 -0
  37. package/src/client/useOpenDirectories.ts +2 -1
  38. package/src/client/usePrettier/index.ts +1 -1
  39. package/src/client/useURLSync.ts +1 -0
  40. package/src/client/utils/scrollUtils.ts +170 -0
  41. package/src/client/vzReducer/searchReducer.ts +6 -3
  42. package/src/server/aiChatHandler/chatOperations.ts +224 -43
  43. package/src/server/aiChatHandler/index.ts +8 -48
  44. package/src/server/aiChatHandler/llmStreaming.ts +149 -170
  45. package/src/types.ts +81 -1
@@ -1,11 +1,14 @@
1
1
  import { dateToTimestamp } from '@vizhub/viz-utils';
2
2
  import { randomId } from '../../randomId.js';
3
- import { ShareDBDoc } from '../../types.js';
3
+ import {
4
+ ShareDBDoc,
5
+ StreamingEvent,
6
+ ExtendedVizContent,
7
+ } from '../../types.js';
4
8
  import { diff } from '../../ot.js';
5
9
  import {
6
10
  VizChatId,
7
11
  VizContent,
8
- VizFileId,
9
12
  VizFiles,
10
13
  } from '@vizhub/viz-types';
11
14
 
@@ -80,6 +83,8 @@ export const addUserMessage = (
80
83
  return userMessage;
81
84
  };
82
85
 
86
+ const DEBUG = false;
87
+
83
88
  /**
84
89
  * Updates AI status in the chat
85
90
  */
@@ -88,6 +93,11 @@ export const updateAIStatus = (
88
93
  chatId: VizChatId,
89
94
  status: string,
90
95
  ) => {
96
+ DEBUG &&
97
+ console.log(
98
+ `ChatOperations: updateAIStatus called with status: "${status}" for chatId: ${chatId}`,
99
+ );
100
+
91
101
  const op = diff(shareDBDoc.data, {
92
102
  ...shareDBDoc.data,
93
103
  chats: {
@@ -98,7 +108,17 @@ export const updateAIStatus = (
98
108
  },
99
109
  },
100
110
  });
111
+
112
+ DEBUG &&
113
+ console.log(
114
+ `ChatOperations: Submitting operation for status update:`,
115
+ op,
116
+ );
101
117
  shareDBDoc.submitOp(op);
118
+ DEBUG &&
119
+ console.log(
120
+ `ChatOperations: Status update operation submitted successfully`,
121
+ );
102
122
  };
103
123
 
104
124
  /**
@@ -405,69 +425,230 @@ export const createNewFile = (
405
425
  return newFileId;
406
426
  };
407
427
 
428
+ // ============================================================================
429
+ // Streaming Chat Operations
430
+ // ============================================================================
431
+
408
432
  /**
409
- * Ensures a file exists, creating it if necessary
433
+ * Creates a streaming AI message with events array
410
434
  */
411
- export const ensureFileExists = (shareDBDoc, fileName) => {
412
- let fileId = resolveFileId(fileName, shareDBDoc);
435
+ export const createStreamingAIMessage = (
436
+ shareDBDoc: ShareDBDoc<ExtendedVizContent>,
437
+ chatId: VizChatId,
438
+ ) => {
439
+ const aiMessage = {
440
+ id: `assistant-${Date.now()}`,
441
+ role: 'assistant',
442
+ content: '',
443
+ timestamp: dateToTimestamp(new Date()),
444
+ streamingEvents: [],
445
+ isProgressive: true,
446
+ };
413
447
 
414
- if (!fileId) {
415
- // File doesn't exist, create it
416
- fileId = createNewFile(shareDBDoc, fileName);
417
- }
448
+ const messageOp = diff(shareDBDoc.data, {
449
+ ...shareDBDoc.data,
450
+ chats: {
451
+ ...shareDBDoc.data.chats,
452
+ [chatId]: {
453
+ ...shareDBDoc.data.chats[chatId],
454
+ messages: [
455
+ ...shareDBDoc.data.chats[chatId].messages,
456
+ aiMessage,
457
+ ],
458
+ updatedAt: dateToTimestamp(new Date()),
459
+ isStreaming: true,
460
+ },
461
+ },
462
+ });
463
+ shareDBDoc.submitOp(messageOp);
418
464
 
419
- return fileId;
465
+ return aiMessage.id;
420
466
  };
421
467
 
422
468
  /**
423
- * Clears the content of a file
469
+ * Adds a streaming event to the most recent AI message
424
470
  */
425
- export const clearFileContent = (
426
- shareDBDoc: ShareDBDoc<VizContent>,
427
- fileId: VizFileId,
471
+ export const addStreamingEvent = (
472
+ shareDBDoc: ShareDBDoc<ExtendedVizContent>,
473
+ chatId: VizChatId,
474
+ event: StreamingEvent,
428
475
  ) => {
429
- const currentFile = shareDBDoc.data.files[fileId];
476
+ DEBUG &&
477
+ console.log(
478
+ `ChatOperations: Adding streaming event:`,
479
+ event,
480
+ );
430
481
 
431
- if (currentFile && currentFile.text) {
432
- // Clear the file content
433
- const newState = {
482
+ const chat = shareDBDoc.data.chats[chatId];
483
+ const messages = [...chat.messages];
484
+ const lastMessageIndex = messages.length - 1;
485
+
486
+ if (
487
+ lastMessageIndex >= 0 &&
488
+ messages[lastMessageIndex].role === 'assistant'
489
+ ) {
490
+ const lastMessage = messages[lastMessageIndex] as any;
491
+ const updatedEvents = [
492
+ ...(lastMessage.streamingEvents || []),
493
+ event,
494
+ ];
495
+
496
+ messages[lastMessageIndex] = {
497
+ ...lastMessage,
498
+ streamingEvents: updatedEvents,
499
+ };
500
+
501
+ const messageOp = diff(shareDBDoc.data, {
434
502
  ...shareDBDoc.data,
435
- files: {
436
- ...shareDBDoc.data.files,
437
- [fileId]: {
438
- ...currentFile,
439
- text: '',
503
+ chats: {
504
+ ...shareDBDoc.data.chats,
505
+ [chatId]: {
506
+ ...chat,
507
+ messages,
508
+ updatedAt: dateToTimestamp(new Date()),
440
509
  },
441
510
  },
442
- };
443
-
444
- const op = diff(shareDBDoc.data, newState);
445
- shareDBDoc.submitOp(op);
511
+ });
512
+ shareDBDoc.submitOp(messageOp);
446
513
  }
447
514
  };
448
515
 
449
516
  /**
450
- * Appends a line to a file using OT operations
517
+ * Updates streaming status for a chat
451
518
  */
452
- export const appendLineToFile = (
453
- shareDBDoc: ShareDBDoc<VizContent>,
454
- fileId: VizFileId,
455
- line: string,
519
+ export const updateStreamingStatus = (
520
+ shareDBDoc: ShareDBDoc<ExtendedVizContent>,
521
+ chatId: VizChatId,
522
+ status: string,
523
+ isStreaming: boolean = true,
456
524
  ) => {
457
- const currentFile = shareDBDoc.data.files[fileId];
458
- const currentContent = currentFile?.text || '';
459
- const newContent = currentContent + line + '\n';
525
+ DEBUG &&
526
+ console.log(
527
+ `ChatOperations: Updating streaming status: "${status}"`,
528
+ );
460
529
 
461
- const newDocState = {
530
+ const op = diff(shareDBDoc.data, {
462
531
  ...shareDBDoc.data,
463
- files: {
464
- ...shareDBDoc.data.files,
465
- [fileId]: {
466
- ...currentFile,
467
- text: newContent,
532
+ chats: {
533
+ ...shareDBDoc.data.chats,
534
+ [chatId]: {
535
+ ...shareDBDoc.data.chats[chatId],
536
+ currentStatus: status,
537
+ isStreaming,
538
+ updatedAt: dateToTimestamp(new Date()),
468
539
  },
469
540
  },
470
- };
541
+ });
542
+ shareDBDoc.submitOp(op);
543
+ };
471
544
 
472
- shareDBDoc.submitOp(diff(shareDBDoc.data, newDocState));
545
+ /**
546
+ * Finalizes streaming message and clears streaming state
547
+ */
548
+ export const finalizeStreamingMessage = (
549
+ shareDBDoc: ShareDBDoc<ExtendedVizContent>,
550
+ chatId: VizChatId,
551
+ ) => {
552
+ DEBUG &&
553
+ console.log(
554
+ `ChatOperations: Finalizing streaming message`,
555
+ );
556
+
557
+ const chat = shareDBDoc.data.chats[chatId];
558
+ const messages = [...chat.messages];
559
+ const lastMessageIndex = messages.length - 1;
560
+
561
+ if (
562
+ lastMessageIndex >= 0 &&
563
+ messages[lastMessageIndex].role === 'assistant'
564
+ ) {
565
+ const lastMessage = messages[lastMessageIndex] as any;
566
+
567
+ messages[lastMessageIndex] = {
568
+ ...lastMessage,
569
+ isComplete: true,
570
+ };
571
+
572
+ const messageOp = diff(shareDBDoc.data, {
573
+ ...shareDBDoc.data,
574
+ chats: {
575
+ ...shareDBDoc.data.chats,
576
+ [chatId]: {
577
+ ...chat,
578
+ messages,
579
+ currentStatus: 'Done',
580
+ isStreaming: false,
581
+ updatedAt: dateToTimestamp(new Date()),
582
+ },
583
+ },
584
+ });
585
+ shareDBDoc.submitOp(messageOp);
586
+ }
473
587
  };
588
+
589
+ // /**
590
+ // * Ensures a file exists, creating it if necessary
591
+ // */
592
+ // export const ensureFileExists = (shareDBDoc, fileName) => {
593
+ // let fileId = resolveFileId(fileName, shareDBDoc);
594
+
595
+ // if (!fileId) {
596
+ // // File doesn't exist, create it
597
+ // fileId = createNewFile(shareDBDoc, fileName);
598
+ // }
599
+
600
+ // return fileId;
601
+ // };
602
+
603
+ // /**
604
+ // * Clears the content of a file
605
+ // */
606
+ // export const clearFileContent = (
607
+ // shareDBDoc: ShareDBDoc<VizContent>,
608
+ // fileId: VizFileId,
609
+ // ) => {
610
+ // const currentFile = shareDBDoc.data.files[fileId];
611
+
612
+ // if (currentFile && currentFile.text) {
613
+ // // Clear the file content
614
+ // const newState = {
615
+ // ...shareDBDoc.data,
616
+ // files: {
617
+ // ...shareDBDoc.data.files,
618
+ // [fileId]: {
619
+ // ...currentFile,
620
+ // text: '',
621
+ // },
622
+ // },
623
+ // };
624
+
625
+ // const op = diff(shareDBDoc.data, newState);
626
+ // shareDBDoc.submitOp(op);
627
+ // }
628
+ // };
629
+
630
+ // /**
631
+ // * Appends a line to a file using OT operations
632
+ // */
633
+ // export const appendLineToFile = (
634
+ // shareDBDoc: ShareDBDoc<VizContent>,
635
+ // fileId: VizFileId,
636
+ // line: string,
637
+ // ) => {
638
+ // const currentFile = shareDBDoc.data.files[fileId];
639
+ // const currentContent = currentFile?.text || '';
640
+ // const newContent = currentContent + line + '\n';
641
+
642
+ // const newDocState = {
643
+ // ...shareDBDoc.data,
644
+ // files: {
645
+ // ...shareDBDoc.data.files,
646
+ // [fileId]: {
647
+ // ...currentFile,
648
+ // text: newContent,
649
+ // },
650
+ // },
651
+ // };
652
+
653
+ // shareDBDoc.submitOp(diff(shareDBDoc.data, newDocState));
654
+ // };
@@ -3,14 +3,10 @@ import {
3
3
  ensureChatsExist,
4
4
  ensureChatExists,
5
5
  addUserMessage,
6
- addDiffToAIMessage,
7
6
  setAIStatus,
8
7
  } from './chatOperations.js';
9
8
  import { createLLMFunction } from './llmStreaming.js';
10
- import {
11
- performAIEditing,
12
- performAIChat,
13
- } from './aiEditing.js';
9
+ import { performAIEditing } from './aiEditing.js';
14
10
  import {
15
11
  handleError,
16
12
  handleBackgroundError,
@@ -27,18 +23,16 @@ export const handleAIChatMessage =
27
23
  ({
28
24
  shareDBDoc,
29
25
  onCreditDeduction,
30
- getCurrentCommitId,
31
26
  model,
32
27
  aiRequestOptions,
33
28
  }: {
34
29
  shareDBDoc: ShareDBDoc<VizContent>;
35
30
  onCreditDeduction?: any;
36
- getCurrentCommitId?: () => string | null;
37
31
  model?: string;
38
32
  aiRequestOptions?: any;
39
33
  }) =>
40
34
  async (req: any, res: any) => {
41
- const { content, chatId, mode = 'edit' } = req.body;
35
+ const { content, chatId } = req.body;
42
36
 
43
37
  if (DEBUG) {
44
38
  console.log(
@@ -67,16 +61,11 @@ export const handleAIChatMessage =
67
61
  // Return success immediately - AI generation continues in background
68
62
  res.status(200).json('success');
69
63
 
70
- // Set AI status to indicate generation is starting
71
- setAIStatus(shareDBDoc, chatId, 'generating');
72
-
73
64
  // Continue AI processing in background (don't await)
74
65
  processAIRequestAsync({
75
66
  shareDBDoc,
76
67
  chatId,
77
68
  content,
78
- mode,
79
- getCurrentCommitId,
80
69
  model,
81
70
  aiRequestOptions,
82
71
  onCreditDeduction,
@@ -100,8 +89,6 @@ const processAIRequestAsync = async ({
100
89
  shareDBDoc,
101
90
  chatId,
102
91
  content,
103
- mode,
104
- getCurrentCommitId,
105
92
  model,
106
93
  aiRequestOptions,
107
94
  onCreditDeduction,
@@ -109,18 +96,11 @@ const processAIRequestAsync = async ({
109
96
  shareDBDoc: ShareDBDoc<VizContent>;
110
97
  chatId: string;
111
98
  content: string;
112
- mode: string;
113
- getCurrentCommitId?: () => string | null;
114
99
  model?: string;
115
100
  aiRequestOptions?: any;
116
101
  onCreditDeduction?: any;
117
102
  }) => {
118
103
  try {
119
- // Capture the current commit ID before making changes (for VizHub integration)
120
- const beforeCommitId = getCurrentCommitId
121
- ? getCurrentCommitId()
122
- : null;
123
-
124
104
  // Create LLM function for streaming
125
105
  const llmFunction = createLLMFunction({
126
106
  shareDBDoc,
@@ -135,32 +115,12 @@ const processAIRequestAsync = async ({
135
115
  const runCode = createRunCodeFunction(submitOperation);
136
116
 
137
117
  // Perform AI editing or chat based on mode
138
- const editResult =
139
- mode === 'ask'
140
- ? await performAIChat({
141
- prompt: content,
142
- shareDBDoc,
143
- llmFunction,
144
- })
145
- : await performAIEditing({
146
- prompt: content,
147
- shareDBDoc,
148
- llmFunction,
149
- runCode,
150
- });
151
-
152
- // Add diff data to the AI message if there are changes
153
- if (
154
- editResult.diffData &&
155
- Object.keys(editResult.diffData).length > 0
156
- ) {
157
- addDiffToAIMessage(
158
- shareDBDoc,
159
- chatId,
160
- editResult.diffData,
161
- beforeCommitId, // Pass the commit ID before AI changes (for VizHub integration)
162
- );
163
- }
118
+ const editResult = await performAIEditing({
119
+ prompt: content,
120
+ shareDBDoc,
121
+ llmFunction,
122
+ runCode,
123
+ });
164
124
 
165
125
  // Handle credit deduction if callback is provided
166
126
  if (onCreditDeduction && editResult.generationId) {