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.
- package/dist/assets/{index-CD0x02Rq.js → index-BfDRE_ou.js} +113 -113
- package/dist/index.html +1 -1
- package/dist/llm-streaming-server/chatOperations.js +40 -10
- package/package.json +1 -1
- package/src/client/CodeEditor/getOrCreateEditor.tsx +6 -1
- package/src/client/CodeEditor/index.tsx +4 -0
- package/src/client/VZCodeContext/useVZCodeState.ts +1 -0
- package/src/client/VZLeft.tsx +17 -2
- package/src/client/VZMiddle.tsx +12 -0
- package/src/client/VZSidebar/VisualEditor/VisualEditor.tsx +101 -48
- package/src/client/VZSidebar/VisualEditor/utils.ts +113 -0
- package/src/client/VZSidebar/index.tsx +23 -6
- package/src/client/aiChatButtonEvent.ts +61 -0
- package/src/client/index.ts +6 -0
- package/src/client/main.jsx +22 -0
- package/src/llm-streaming-server/chatOperations.ts +53 -12
- package/src/llm-streaming-server/llmStreaming.ts +0 -3
- package/src/llm-streaming-ui/components/Message.tsx +0 -2
|
@@ -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
|
+
}
|
package/src/client/index.ts
CHANGED
|
@@ -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,
|
package/src/client/main.jsx
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
//
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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[
|
|
393
|
+
...messages[targetMessageIndex],
|
|
357
394
|
diffData,
|
|
358
|
-
...(beforeCommitId && { beforeCommitId }),
|
|
395
|
+
...(beforeCommitId && { beforeCommitId }),
|
|
359
396
|
};
|
|
360
|
-
|
|
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';
|