vzcode 2.30.0 → 2.31.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.
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Host-facing event emitted when the VZCode sidebar "Edit with AI"
3
+ * button is clicked in "emit" mode.
4
+ *
5
+ * A host opts in by passing `aiChatButtonEvent` to `VZSidebar` /
6
+ * `VZLeft`. When set, clicking `#ai-chat-icon` dispatches this event
7
+ * instead of opening VZCode's built-in AI chat panel, so the host can
8
+ * route the click to its own AI editing entry point (e.g. VizHub
9
+ * Studio's top-bar "AI Edit" action).
10
+ *
11
+ * The constant is exported so hosts don't hardcode the event name.
12
+ */
13
+ export const AI_CHAT_BUTTON_EVENT =
14
+ 'vzcode:ai-chat-button-click';
15
+
16
+ export interface AIChatButtonEventDetail {
17
+ source: 'vzcode-sidebar';
18
+ }
19
+
20
+ /**
21
+ * Dispatch the host-facing AI chat button event.
22
+ * Kept as a standalone function so it is unit-testable without a DOM.
23
+ */
24
+ export function emitAIChatButtonEvent(
25
+ eventName: string,
26
+ target: EventTarget,
27
+ ): void {
28
+ target.dispatchEvent(
29
+ new CustomEvent<AIChatButtonEventDetail>(eventName, {
30
+ bubbles: true,
31
+ composed: true,
32
+ detail: { source: 'vzcode-sidebar' },
33
+ }),
34
+ );
35
+ }
36
+
37
+ /**
38
+ * Decide what clicking the sidebar AI chat button should do.
39
+ *
40
+ * - When `aiChatButtonEvent` is set, emit the event and leave all
41
+ * VZCode state untouched (the host owns the action).
42
+ * - Otherwise, fall back to opening VZCode's built-in AI chat.
43
+ *
44
+ * Extracted from the component so the branching is unit-testable
45
+ * without rendering or a real DOM.
46
+ */
47
+ export function handleAIChatButtonClick({
48
+ aiChatButtonEvent,
49
+ target,
50
+ openAIChat,
51
+ }: {
52
+ aiChatButtonEvent?: string;
53
+ target: EventTarget;
54
+ openAIChat: () => void;
55
+ }): void {
56
+ if (aiChatButtonEvent) {
57
+ emitAIChatButtonEvent(aiChatButtonEvent, target);
58
+ return;
59
+ }
60
+ openAIChat();
61
+ }
@@ -11,6 +11,12 @@ export { VZKeyboardShortcutsDoc } from './VZKeyboardShortcutsDoc';
11
11
  export { VZResizer } from './VZResizer';
12
12
  export { VZLeft } from './VZLeft';
13
13
  export { VZMiddle } from './VZMiddle';
14
+ export {
15
+ AI_CHAT_BUTTON_EVENT,
16
+ emitAIChatButtonEvent,
17
+ handleAIChatButtonClick,
18
+ } from './aiChatButtonEvent';
19
+ export type { AIChatButtonEventDetail } from './aiChatButtonEvent';
14
20
  export {
15
21
  SplitPaneResizeProvider,
16
22
  SplitPaneResizeContext,
@@ -3,6 +3,28 @@ import * as ReactDOM from 'react-dom/client';
3
3
  import App from './App';
4
4
  import { BrowserRouter } from 'react-router-dom';
5
5
 
6
+ // Global error handler to catch CodeMirror DOM position errors
7
+ // These can occur when CodeMirror instances are created but not yet
8
+ // attached to the DOM, or when presence indicators try to render
9
+ // on offscreen editor instances.
10
+ window.addEventListener('error', (event) => {
11
+ // Check if this is the specific CodeMirror posFromDOM/posAtDOM error
12
+ if (
13
+ event.error instanceof RangeError &&
14
+ event.error.message.includes(
15
+ 'Trying to find position for a DOM position outside of the document',
16
+ )
17
+ ) {
18
+ // Log the error for debugging purposes
19
+ console.log(
20
+ 'Suppressed CodeMirror DOM position error:',
21
+ event.error,
22
+ );
23
+ // Prevent the error from being logged to console by returning true
24
+ return true;
25
+ }
26
+ });
27
+
6
28
  ReactDOM.createRoot(document.getElementById('root')).render(
7
29
  <BrowserRouter>
8
30
  <App />
@@ -335,30 +335,66 @@ export const finalizeAIMessage = (
335
335
  };
336
336
 
337
337
  /**
338
- * Adds diff data to the most recent AI message
338
+ * Adds diff data to a specific AI message by ID
339
+ * Phase 3 Fix: Use explicit message ID instead of assuming
340
+ * last message is AI, preventing bugs with rapid message sends
339
341
  */
340
342
  export const addDiffToAIMessage = (
341
343
  shareDBDoc: ShareDBDoc<VizContent>,
342
344
  chatId: VizChatId,
343
345
  diffData: any,
344
- beforeCommitId?: string, // Commit ID before AI changes for VizHub integration
346
+ beforeCommitId?: string, // Commit ID before AI changes
347
+ messageId?: string, // Explicit AI message ID to update
345
348
  ) => {
346
349
  const chat = shareDBDoc.data.chats[chatId];
350
+ if (!chat || !chat.messages) {
351
+ console.warn(
352
+ `Chat ${chatId} not found or has no messages`,
353
+ );
354
+ return;
355
+ }
356
+
347
357
  const messages = [...chat.messages];
358
+ let targetMessageIndex = -1;
348
359
 
349
- // Find the most recent AI message
350
- const lastAIMessageIndex = messages.length - 1;
351
- if (
352
- lastAIMessageIndex >= 0 &&
353
- messages[lastAIMessageIndex].role === 'assistant'
354
- ) {
360
+ // Phase 3 Fix: Use explicit message ID if provided
361
+ if (messageId) {
362
+ targetMessageIndex = messages.findIndex(
363
+ (msg) => msg.id === messageId,
364
+ );
365
+ if (
366
+ targetMessageIndex === -1 ||
367
+ messages[targetMessageIndex].role !== 'assistant'
368
+ ) {
369
+ console.warn(
370
+ `AI message with id ${messageId} not found in chat ${chatId}`,
371
+ );
372
+ return;
373
+ }
374
+ } else {
375
+ // Fallback: Find the most recent AI message (safer than
376
+ // just assuming last message)
377
+ for (let i = messages.length - 1; i >= 0; i--) {
378
+ if (messages[i].role === 'assistant') {
379
+ targetMessageIndex = i;
380
+ break;
381
+ }
382
+ }
383
+
384
+ if (targetMessageIndex === -1) {
385
+ console.warn(`No AI message found in chat ${chatId}`);
386
+ return;
387
+ }
388
+ }
389
+
390
+ // Update the target message with diff data
391
+ if (targetMessageIndex >= 0) {
355
392
  const newMessage = {
356
- ...messages[lastAIMessageIndex],
393
+ ...messages[targetMessageIndex],
357
394
  diffData,
358
- ...(beforeCommitId && { beforeCommitId }), // Add beforeCommitId for VizHub integration
395
+ ...(beforeCommitId && { beforeCommitId }),
359
396
  };
360
- // Use type assertion to extend the message with diffData
361
- (messages[lastAIMessageIndex] as any) = newMessage;
397
+ (messages[targetMessageIndex] as any) = newMessage;
362
398
 
363
399
  const messageOp = diff(shareDBDoc.data, {
364
400
  ...shareDBDoc.data,
@@ -373,6 +409,11 @@ export const addDiffToAIMessage = (
373
409
  });
374
410
 
375
411
  shareDBDoc.submitOp(messageOp);
412
+
413
+ DEBUG &&
414
+ console.log(
415
+ `addDiffToAIMessage: Successfully added diff to message ${targetMessageIndex} in chat ${chatId}`,
416
+ );
376
417
  }
377
418
  };
378
419
 
@@ -78,7 +78,6 @@ export const createLLMFunction = ({
78
78
  },
79
79
  });
80
80
 
81
-
82
81
  let fullContent = '';
83
82
  let generationId = '';
84
83
  let currentEditingFileName = null;
@@ -253,8 +252,6 @@ export const createLLMFunction = ({
253
252
  const chunks = [];
254
253
  let reasoningContent = '';
255
254
 
256
-
257
-
258
255
  // Configure reasoning tokens based on enableReasoningTokens flag
259
256
  const requestConfig: any = {
260
257
  model: modelName,
@@ -13,8 +13,6 @@ import { DiffView, DiffViewRef } from './DiffView';
13
13
  import { UnifiedFilesDiff } from '../../utils/fileDiff';
14
14
  import { enableDiffView } from '../../client/featureFlags';
15
15
 
16
- const DEBUG = true;
17
-
18
16
  interface MessageProps {
19
17
  id: string;
20
18
  role: 'user' | 'assistant';