vzcode 2.21.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-D6-he0oi.js → index-CfDs-dAu.js} +99 -99
- package/dist/assets/{index-fYq6iaqF.css → index-fSroLVgi.css} +1 -1
- 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/index.js +5 -5
- package/package.json +35 -35
- package/src/client/CodeEditor/getOrCreateEditor.tsx +20 -13
- package/src/client/CodeEditor/index.tsx +3 -3
- 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/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/index.ts +5 -5
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
import { parseMarkdownFiles, StreamingMarkdownParser, } from 'llm-code-format';
|
|
3
|
+
import { mergeFileChanges } from 'editcodewithai';
|
|
4
|
+
import { updateFiles, updateAIScratchpad, createStreamingAIMessage, addStreamingEvent, updateStreamingStatus, finalizeStreamingMessage, } from './chatOperations.js';
|
|
5
|
+
import { formatFiles } from '../server/prettier.js';
|
|
6
|
+
// Verbose logs
|
|
7
|
+
const DEBUG = false;
|
|
8
|
+
// Useful for testing/debugging the streaming behavior
|
|
9
|
+
const slowMode = false;
|
|
10
|
+
// If the `EMIT_FIXTURES` variable is true,
|
|
11
|
+
// then an output file in the `test/fixtures` folder
|
|
12
|
+
// with the before and after file states for testing purposes.
|
|
13
|
+
// This feeds into tests in codemirror-ot.
|
|
14
|
+
const EMIT_FIXTURES = false;
|
|
15
|
+
/**
|
|
16
|
+
* Creates and configures the LLM function for streaming with reasoning tokens
|
|
17
|
+
*/
|
|
18
|
+
export const createLLMFunction = ({ shareDBDoc, chatId,
|
|
19
|
+
// Feature flag to enable/disable reasoning tokens.
|
|
20
|
+
// When false, reasoning tokens are not requested from the API
|
|
21
|
+
// and reasoning content is not processed in the streaming response.
|
|
22
|
+
enableReasoningTokens = false, model, aiRequestOptions, }) => {
|
|
23
|
+
return async (fullPrompt) => {
|
|
24
|
+
// Create OpenRouter client for reasoning token support
|
|
25
|
+
const openRouterClient = new OpenAI({
|
|
26
|
+
apiKey: process.env.VZCODE_EDIT_WITH_AI_API_KEY,
|
|
27
|
+
baseURL: process.env.VZCODE_EDIT_WITH_AI_BASE_URL ||
|
|
28
|
+
'https://openrouter.ai/api/v1',
|
|
29
|
+
defaultHeaders: {
|
|
30
|
+
'HTTP-Referer': 'https://vizhub.com',
|
|
31
|
+
'X-Title': 'VizHub',
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
let fullContent = '';
|
|
35
|
+
let generationId = '';
|
|
36
|
+
let currentEditingFileName = null;
|
|
37
|
+
let accumulatedTextChunk = '';
|
|
38
|
+
let currentFileContent = '';
|
|
39
|
+
// Create streaming AI message
|
|
40
|
+
createStreamingAIMessage(shareDBDoc, chatId);
|
|
41
|
+
// Set initial content generation status
|
|
42
|
+
updateStreamingStatus(shareDBDoc, chatId, 'Formulating a plan...');
|
|
43
|
+
// Helper to get original file content
|
|
44
|
+
const getOriginalFileContent = (fileName) => {
|
|
45
|
+
const files = shareDBDoc.data.files;
|
|
46
|
+
for (const file of Object.values(files)) {
|
|
47
|
+
if (file.name === fileName) {
|
|
48
|
+
return file.text || '';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return '';
|
|
52
|
+
};
|
|
53
|
+
// Helper to emit text chunk when accumulated
|
|
54
|
+
const emitTextChunk = async () => {
|
|
55
|
+
if (accumulatedTextChunk.trim()) {
|
|
56
|
+
DEBUG &&
|
|
57
|
+
console.log('LLMStreaming: Emitting text chunk:', accumulatedTextChunk.substring(0, 100) + '...');
|
|
58
|
+
await addStreamingEvent(shareDBDoc, chatId, {
|
|
59
|
+
type: 'text_chunk',
|
|
60
|
+
content: accumulatedTextChunk,
|
|
61
|
+
timestamp: Date.now(),
|
|
62
|
+
});
|
|
63
|
+
accumulatedTextChunk = '';
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
// Helper to complete file editing
|
|
67
|
+
const completeFileEditing = async (fileName) => {
|
|
68
|
+
if (fileName) {
|
|
69
|
+
DEBUG &&
|
|
70
|
+
console.log(`LLMStreaming: Completing file editing for ${fileName}`);
|
|
71
|
+
await addStreamingEvent(shareDBDoc, chatId, {
|
|
72
|
+
type: 'file_complete',
|
|
73
|
+
fileName,
|
|
74
|
+
beforeContent: getOriginalFileContent(fileName),
|
|
75
|
+
afterContent: currentFileContent,
|
|
76
|
+
timestamp: Date.now(),
|
|
77
|
+
});
|
|
78
|
+
currentFileContent = '';
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
// Define callbacks for streaming parser
|
|
82
|
+
const callbacks = {
|
|
83
|
+
onFileNameChange: async (fileName, format) => {
|
|
84
|
+
DEBUG &&
|
|
85
|
+
console.log(`LLMStreaming: File changed to: ${fileName} (${format})`);
|
|
86
|
+
// Emit any accumulated text chunk first
|
|
87
|
+
await emitTextChunk();
|
|
88
|
+
// Complete previous file if any
|
|
89
|
+
if (currentEditingFileName) {
|
|
90
|
+
await completeFileEditing(currentEditingFileName);
|
|
91
|
+
}
|
|
92
|
+
// Start new file
|
|
93
|
+
currentEditingFileName = fileName;
|
|
94
|
+
currentFileContent = '';
|
|
95
|
+
// Emit file start event
|
|
96
|
+
await addStreamingEvent(shareDBDoc, chatId, {
|
|
97
|
+
type: 'file_start',
|
|
98
|
+
fileName,
|
|
99
|
+
timestamp: Date.now(),
|
|
100
|
+
});
|
|
101
|
+
// Update status
|
|
102
|
+
updateStreamingStatus(shareDBDoc, chatId, `Editing ${fileName}...`);
|
|
103
|
+
},
|
|
104
|
+
onCodeLine: async (line) => {
|
|
105
|
+
DEBUG && console.log(`Code line: ${line}`);
|
|
106
|
+
// Accumulate code content for the current file
|
|
107
|
+
currentFileContent += line + '\n';
|
|
108
|
+
},
|
|
109
|
+
onNonCodeLine: async (line) => {
|
|
110
|
+
DEBUG && console.log(`Non-code line: ${line}`);
|
|
111
|
+
// Accumulate non-code content as text chunk
|
|
112
|
+
if (line.trim() !== '') {
|
|
113
|
+
accumulatedTextChunk += line + '\n';
|
|
114
|
+
// Update status for subsequent non-code chunks
|
|
115
|
+
if (firstNonCodeChunkProcessed) {
|
|
116
|
+
updateStreamingStatus(shareDBDoc, chatId, 'Describing changes...');
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
firstNonCodeChunkProcessed = true;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
onFileDelete: async (fileName) => {
|
|
124
|
+
DEBUG &&
|
|
125
|
+
console.log(`LLMStreaming: File marked for deletion: ${fileName}`);
|
|
126
|
+
// Emit any accumulated text chunk first
|
|
127
|
+
await emitTextChunk();
|
|
128
|
+
// Complete previous file if any
|
|
129
|
+
if (currentEditingFileName) {
|
|
130
|
+
await completeFileEditing(currentEditingFileName);
|
|
131
|
+
}
|
|
132
|
+
// Reset current editing state
|
|
133
|
+
currentEditingFileName = null;
|
|
134
|
+
currentFileContent = '';
|
|
135
|
+
// Emit file delete event
|
|
136
|
+
await addStreamingEvent(shareDBDoc, chatId, {
|
|
137
|
+
type: 'file_delete',
|
|
138
|
+
fileName,
|
|
139
|
+
timestamp: Date.now(),
|
|
140
|
+
});
|
|
141
|
+
// Update status
|
|
142
|
+
updateStreamingStatus(shareDBDoc, chatId, `Deleting ${fileName}...`);
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
const parser = new StreamingMarkdownParser(callbacks);
|
|
146
|
+
const chunks = [];
|
|
147
|
+
let reasoningContent = '';
|
|
148
|
+
// Stream the response with reasoning tokens
|
|
149
|
+
const modelName = model ||
|
|
150
|
+
process.env.VZCODE_EDIT_WITH_AI_MODEL_NAME ||
|
|
151
|
+
'anthropic/claude-3.5-sonnet';
|
|
152
|
+
// Configure reasoning tokens based on enableReasoningTokens flag
|
|
153
|
+
const requestConfig = {
|
|
154
|
+
model: modelName,
|
|
155
|
+
messages: [{ role: 'user', content: fullPrompt }],
|
|
156
|
+
usage: { include: true },
|
|
157
|
+
stream: true,
|
|
158
|
+
...aiRequestOptions,
|
|
159
|
+
};
|
|
160
|
+
// Only include reasoning configuration if reasoning tokens are enabled
|
|
161
|
+
if (enableReasoningTokens) {
|
|
162
|
+
requestConfig.reasoning = {
|
|
163
|
+
effort: 'low',
|
|
164
|
+
exclude: false,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const stream = await openRouterClient.chat.completions.create(requestConfig);
|
|
168
|
+
let reasoningStarted = false;
|
|
169
|
+
let contentStarted = false;
|
|
170
|
+
let firstNonCodeChunkProcessed = false;
|
|
171
|
+
for await (const chunk of stream) {
|
|
172
|
+
if (slowMode) {
|
|
173
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
174
|
+
}
|
|
175
|
+
const delta = chunk.choices[0]?.delta; // Type assertion for OpenRouter-specific reasoning fields
|
|
176
|
+
if (delta?.reasoning && enableReasoningTokens) {
|
|
177
|
+
// Handle reasoning tokens (thinking) - only if enabled
|
|
178
|
+
if (!reasoningStarted) {
|
|
179
|
+
reasoningStarted = true;
|
|
180
|
+
updateStreamingStatus(shareDBDoc, chatId, 'Thinking...');
|
|
181
|
+
}
|
|
182
|
+
reasoningContent += delta.reasoning;
|
|
183
|
+
updateAIScratchpad(shareDBDoc, chatId, reasoningContent);
|
|
184
|
+
}
|
|
185
|
+
else if (delta?.content) {
|
|
186
|
+
// Handle regular content tokens
|
|
187
|
+
if (!contentStarted) {
|
|
188
|
+
contentStarted = true;
|
|
189
|
+
if (reasoningStarted) {
|
|
190
|
+
// Clear reasoning when content starts
|
|
191
|
+
updateAIScratchpad(shareDBDoc, chatId, '');
|
|
192
|
+
}
|
|
193
|
+
// // Set initial content generation status
|
|
194
|
+
// updateStreamingStatus(
|
|
195
|
+
// shareDBDoc,
|
|
196
|
+
// chatId,
|
|
197
|
+
// 'Formulating a plan...',
|
|
198
|
+
// );
|
|
199
|
+
}
|
|
200
|
+
const chunkContent = delta.content;
|
|
201
|
+
chunks.push(chunkContent);
|
|
202
|
+
await parser.processChunk(chunkContent);
|
|
203
|
+
fullContent += chunkContent;
|
|
204
|
+
}
|
|
205
|
+
else if (chunk.usage) {
|
|
206
|
+
// Handle usage information
|
|
207
|
+
DEBUG && console.log('Usage:', chunk.usage);
|
|
208
|
+
}
|
|
209
|
+
if (!generationId && chunk.id) {
|
|
210
|
+
generationId = chunk.id;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
await parser.flushRemaining();
|
|
214
|
+
// Emit any remaining text chunk
|
|
215
|
+
await emitTextChunk();
|
|
216
|
+
// Complete final file if any
|
|
217
|
+
if (currentEditingFileName) {
|
|
218
|
+
await completeFileEditing(currentEditingFileName);
|
|
219
|
+
}
|
|
220
|
+
// // Capture the current state of files before applying changes
|
|
221
|
+
// const beforeFiles = createFilesSnapshot(
|
|
222
|
+
// shareDBDoc.data.files,
|
|
223
|
+
// );
|
|
224
|
+
// Parse the full content to extract file changes
|
|
225
|
+
// export type FileCollection = Record<string, string>;
|
|
226
|
+
const newFilesUnformatted = parseMarkdownFiles(fullContent, 'bold').files;
|
|
227
|
+
// Run Prettier on `newFiles` before applying them,
|
|
228
|
+
// preserving empty files as empty
|
|
229
|
+
// since that is the cue to delete a file.
|
|
230
|
+
const newFilesFormatted = await formatFiles(newFilesUnformatted);
|
|
231
|
+
// Capture the current state of files before applying changes
|
|
232
|
+
let vizFilesBefore;
|
|
233
|
+
if (EMIT_FIXTURES) {
|
|
234
|
+
vizFilesBefore = JSON.parse(JSON.stringify(shareDBDoc.data.files));
|
|
235
|
+
}
|
|
236
|
+
// Apply all the edits at once
|
|
237
|
+
const vizFilesAfter = mergeFileChanges(shareDBDoc.data.files, newFilesFormatted);
|
|
238
|
+
const filesOp = updateFiles(shareDBDoc, vizFilesAfter);
|
|
239
|
+
if (EMIT_FIXTURES) {
|
|
240
|
+
const fs = await import('fs');
|
|
241
|
+
const path = await import('path');
|
|
242
|
+
const testCasesDir = path.resolve(process.cwd(), '../', 'fixtures');
|
|
243
|
+
if (!fs.existsSync(testCasesDir)) {
|
|
244
|
+
fs.mkdirSync(testCasesDir, { recursive: true });
|
|
245
|
+
}
|
|
246
|
+
const timestamp = new Date()
|
|
247
|
+
.toISOString()
|
|
248
|
+
.replace(/[:.]/g, '-');
|
|
249
|
+
const testCasePath = path.join(testCasesDir, `ai-chat-${timestamp}.json`);
|
|
250
|
+
const testCaseData = {
|
|
251
|
+
vizFilesBefore,
|
|
252
|
+
vizFilesAfter,
|
|
253
|
+
filesOp,
|
|
254
|
+
};
|
|
255
|
+
fs.writeFileSync(testCasePath, JSON.stringify(testCaseData, null, 2));
|
|
256
|
+
console.log(`AI chat test case written to ${testCasePath}`);
|
|
257
|
+
}
|
|
258
|
+
// Finalize streaming message
|
|
259
|
+
finalizeStreamingMessage(shareDBDoc, chatId);
|
|
260
|
+
return {
|
|
261
|
+
content: fullContent,
|
|
262
|
+
generationId: generationId,
|
|
263
|
+
};
|
|
264
|
+
};
|
|
265
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates incoming request data for AI chat messages
|
|
3
|
+
*/
|
|
4
|
+
export const validateRequest = (req, res) => {
|
|
5
|
+
const { content, chatId } = req.body;
|
|
6
|
+
if (!content || typeof content !== 'string') {
|
|
7
|
+
res.status(400).json({
|
|
8
|
+
error: 'Invalid request: content is required and must be a string',
|
|
9
|
+
});
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
if (!chatId || typeof chatId !== 'string') {
|
|
13
|
+
res.status(400).json({
|
|
14
|
+
error: 'Invalid request: chatId is required and must be a string',
|
|
15
|
+
});
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return true;
|
|
19
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { validateRequest } from '
|
|
2
|
-
import { ensureChatsExist, ensureChatExists, addUserMessage, setAIStatus, } from '
|
|
3
|
-
import { createLLMFunction } from '
|
|
4
|
-
import { performAIEditing } from '
|
|
5
|
-
import { handleError, handleBackgroundError, } from '
|
|
1
|
+
import { validateRequest } from '../../llm-streaming-server/validation.js';
|
|
2
|
+
import { ensureChatsExist, ensureChatExists, addUserMessage, setAIStatus, } from '../../llm-streaming-server/chatOperations.js';
|
|
3
|
+
import { createLLMFunction } from '../../llm-streaming-server/llmStreaming.js';
|
|
4
|
+
import { performAIEditing } from '../../llm-streaming-server/aiEditing.js';
|
|
5
|
+
import { handleError, handleBackgroundError, } from '../../llm-streaming-server/errorHandling.js';
|
|
6
6
|
import { createRunCodeFunction } from '../../runCode.js';
|
|
7
7
|
import { createSubmitOperation } from '../../submitOperation.js';
|
|
8
8
|
import { getGenerationMetadata } from 'editcodewithai';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vzcode",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.22.0",
|
|
4
4
|
"description": "Multiplayer code editor system",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -110,35 +110,35 @@
|
|
|
110
110
|
},
|
|
111
111
|
"homepage": "https://github.com/vizhub-core/vzcode#readme",
|
|
112
112
|
"dependencies": {
|
|
113
|
-
"@codemirror/autocomplete": "^6.19.
|
|
113
|
+
"@codemirror/autocomplete": "^6.19.1",
|
|
114
114
|
"@codemirror/lang-css": "^6.3.1",
|
|
115
115
|
"@codemirror/lang-html": "^6.4.11",
|
|
116
116
|
"@codemirror/lang-javascript": "^6.2.4",
|
|
117
117
|
"@codemirror/lang-json": "^6.0.2",
|
|
118
|
-
"@codemirror/lang-markdown": "^6.
|
|
119
|
-
"@codemirror/lint": "^6.9.
|
|
118
|
+
"@codemirror/lang-markdown": "^6.5.0",
|
|
119
|
+
"@codemirror/lint": "^6.9.2",
|
|
120
120
|
"@codemirror/state": "^6.5.2",
|
|
121
121
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
122
|
-
"@codemirror/view": "^6.38.
|
|
123
|
-
"@langchain/core": "^0.3
|
|
124
|
-
"@langchain/openai": "^0.
|
|
125
|
-
"@lezer/highlight": "^1.2.
|
|
122
|
+
"@codemirror/view": "^6.38.6",
|
|
123
|
+
"@langchain/core": "^1.0.3",
|
|
124
|
+
"@langchain/openai": "^1.0.0",
|
|
125
|
+
"@lezer/highlight": "^1.2.3",
|
|
126
126
|
"@livekit/components-react": "^2.9.15",
|
|
127
127
|
"@replit/codemirror-indentation-markers": "^6.5.3",
|
|
128
128
|
"@replit/codemirror-interact": "^6.3.1",
|
|
129
129
|
"@replit/codemirror-lang-svelte": "^6.0.0",
|
|
130
130
|
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
|
131
131
|
"@teamwork/websocket-json-stream": "^2.0.0",
|
|
132
|
-
"@typescript/vfs": "^1.6.
|
|
133
|
-
"@uiw/codemirror-theme-abcdef": "^4.25.
|
|
134
|
-
"@uiw/codemirror-theme-dracula": "^4.25.
|
|
135
|
-
"@uiw/codemirror-theme-eclipse": "^4.25.
|
|
136
|
-
"@uiw/codemirror-theme-github": "^4.25.
|
|
137
|
-
"@uiw/codemirror-theme-material": "^4.25.
|
|
138
|
-
"@uiw/codemirror-theme-nord": "^4.25.
|
|
139
|
-
"@uiw/codemirror-theme-okaidia": "^4.25.
|
|
140
|
-
"@uiw/codemirror-theme-xcode": "^4.25.
|
|
141
|
-
"@uiw/codemirror-themes": "^4.25.
|
|
132
|
+
"@typescript/vfs": "^1.6.2",
|
|
133
|
+
"@uiw/codemirror-theme-abcdef": "^4.25.3",
|
|
134
|
+
"@uiw/codemirror-theme-dracula": "^4.25.3",
|
|
135
|
+
"@uiw/codemirror-theme-eclipse": "^4.25.3",
|
|
136
|
+
"@uiw/codemirror-theme-github": "^4.25.3",
|
|
137
|
+
"@uiw/codemirror-theme-material": "^4.25.3",
|
|
138
|
+
"@uiw/codemirror-theme-nord": "^4.25.3",
|
|
139
|
+
"@uiw/codemirror-theme-okaidia": "^4.25.3",
|
|
140
|
+
"@uiw/codemirror-theme-xcode": "^4.25.3",
|
|
141
|
+
"@uiw/codemirror-themes": "^4.25.3",
|
|
142
142
|
"@valtown/codemirror-ts": "^2.3.1",
|
|
143
143
|
"@vizhub/runtime": "^4.5.0",
|
|
144
144
|
"@vizhub/viz-types": "^0.5.0",
|
|
@@ -157,14 +157,14 @@
|
|
|
157
157
|
"diff2html": "^3.4.52",
|
|
158
158
|
"dotenv": "^17.2.3",
|
|
159
159
|
"editcodewithai": "^2.4.0",
|
|
160
|
-
"eslint-linter-browserify": "^9.
|
|
160
|
+
"eslint-linter-browserify": "^9.39.1",
|
|
161
161
|
"express": "^5.1.0",
|
|
162
162
|
"ignore": "^7.0.5",
|
|
163
163
|
"json0-ot-diff": "^1.1.2",
|
|
164
164
|
"jszip": "^3.10.1",
|
|
165
165
|
"livekit-server-sdk": "^2.14.0",
|
|
166
166
|
"llm-code-format": "^3.1.0",
|
|
167
|
-
"lucide-react": "^0.
|
|
167
|
+
"lucide-react": "^0.553.0",
|
|
168
168
|
"npm": "^11.6.2",
|
|
169
169
|
"open": "^10.2.0",
|
|
170
170
|
"prettier-plugin-svelte": "^3.4.0",
|
|
@@ -172,7 +172,7 @@
|
|
|
172
172
|
"react-bootstrap": "^2.10.10",
|
|
173
173
|
"react-dom": "^18",
|
|
174
174
|
"react-markdown": "^10.1.0",
|
|
175
|
-
"react-router-dom": "^7.9.
|
|
175
|
+
"react-router-dom": "^7.9.5",
|
|
176
176
|
"remark-gfm": "^4.0.1",
|
|
177
177
|
"sharedb": "^5.2.2",
|
|
178
178
|
"sharedb-client-browser": "^5.2.2",
|
|
@@ -181,31 +181,31 @@
|
|
|
181
181
|
"ws": "^8.18.3"
|
|
182
182
|
},
|
|
183
183
|
"devDependencies": {
|
|
184
|
-
"@eslint/js": "^9.
|
|
185
|
-
"@tauri-apps/cli": "^2.
|
|
184
|
+
"@eslint/js": "^9.39.1",
|
|
185
|
+
"@tauri-apps/cli": "^2.9.3",
|
|
186
186
|
"@types/d3-color": "^3.1.3",
|
|
187
187
|
"@types/react": "^18",
|
|
188
188
|
"@types/react-dom": "^18",
|
|
189
|
-
"@typescript-eslint/eslint-plugin": "^8.46.
|
|
190
|
-
"@typescript-eslint/parser": "^8.46.
|
|
191
|
-
"@vitejs/plugin-react": "^5.0
|
|
189
|
+
"@typescript-eslint/eslint-plugin": "^8.46.3",
|
|
190
|
+
"@typescript-eslint/parser": "^8.46.3",
|
|
191
|
+
"@vitejs/plugin-react": "^5.1.0",
|
|
192
192
|
"concurrently": "^9.2.1",
|
|
193
193
|
"cross-env": "^10.1.0",
|
|
194
|
-
"eslint": "^9.
|
|
194
|
+
"eslint": "^9.39.1",
|
|
195
195
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
196
196
|
"eslint-plugin-react": "^7.37.5",
|
|
197
|
-
"eslint-plugin-react-hooks": "^7.0.
|
|
198
|
-
"globals": "^16.
|
|
199
|
-
"npm-check-updates": "^19.
|
|
197
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
198
|
+
"globals": "^16.5.0",
|
|
199
|
+
"npm-check-updates": "^19.1.2",
|
|
200
200
|
"prettier": "^3.6.2",
|
|
201
|
-
"sass": "^1.93.
|
|
201
|
+
"sass": "^1.93.3",
|
|
202
202
|
"ts-node": "^10.9.2",
|
|
203
203
|
"typescript": "^5.9.3",
|
|
204
|
-
"vite": "^7.
|
|
205
|
-
"vitest": "^
|
|
204
|
+
"vite": "^7.2.2",
|
|
205
|
+
"vitest": "^4.0.8"
|
|
206
206
|
},
|
|
207
207
|
"optionalDependencies": {
|
|
208
|
-
"@rollup/rollup-darwin-arm64": "^4.
|
|
209
|
-
"@rollup/rollup-win32-x64-msvc": "^4.
|
|
208
|
+
"@rollup/rollup-darwin-arm64": "^4.53.1",
|
|
209
|
+
"@rollup/rollup-win32-x64-msvc": "^4.53.1"
|
|
210
210
|
}
|
|
211
211
|
}
|
|
@@ -70,7 +70,7 @@ import { SparklesSVG } from '../Icons/SparklesSVG';
|
|
|
70
70
|
import { MINIMAL_EXTENSIONS } from '../featureFlags';
|
|
71
71
|
|
|
72
72
|
const DEBUG = false;
|
|
73
|
-
const enableToDoPlugin =
|
|
73
|
+
const enableToDoPlugin = true;
|
|
74
74
|
|
|
75
75
|
// Define a StateField to store the file name.
|
|
76
76
|
// This should be defined at the module level if it's to be imported by other modules.
|
|
@@ -138,6 +138,7 @@ const languageExtensions = {
|
|
|
138
138
|
html: () => html(htmlConfig),
|
|
139
139
|
css,
|
|
140
140
|
md: markdown,
|
|
141
|
+
prompt: markdown,
|
|
141
142
|
svelte,
|
|
142
143
|
};
|
|
143
144
|
|
|
@@ -188,7 +189,7 @@ export const getOrCreateEditor = async ({
|
|
|
188
189
|
esLintSource,
|
|
189
190
|
rainbowBracketsEnabled = true,
|
|
190
191
|
setIsAIChatOpen,
|
|
191
|
-
|
|
192
|
+
setAIChatMessage,
|
|
192
193
|
}: {
|
|
193
194
|
// TODO pass this in from the outside
|
|
194
195
|
paneId?: PaneId;
|
|
@@ -225,7 +226,7 @@ export const getOrCreateEditor = async ({
|
|
|
225
226
|
) => Promise<readonly Diagnostic[]>;
|
|
226
227
|
rainbowBracketsEnabled?: boolean; // New parameter type
|
|
227
228
|
setIsAIChatOpen: (isAIChatOpen: boolean) => void;
|
|
228
|
-
|
|
229
|
+
setAIChatMessage: (message: string) => void;
|
|
229
230
|
}): Promise<ExtendedEditorCacheValue> => {
|
|
230
231
|
// Cache hit
|
|
231
232
|
|
|
@@ -356,7 +357,10 @@ export const getOrCreateEditor = async ({
|
|
|
356
357
|
);
|
|
357
358
|
|
|
358
359
|
// Enable line wrapping for Markdown files
|
|
359
|
-
if (
|
|
360
|
+
if (
|
|
361
|
+
fileExtension === 'md' ||
|
|
362
|
+
fileExtension === 'prompt'
|
|
363
|
+
) {
|
|
360
364
|
extensions.push(EditorView.lineWrapping);
|
|
361
365
|
}
|
|
362
366
|
|
|
@@ -485,22 +489,25 @@ export const getOrCreateEditor = async ({
|
|
|
485
489
|
wrap.style.alignItems = 'center';
|
|
486
490
|
wrap.style.justifyContent = 'center';
|
|
487
491
|
wrap.className = 'icon-button icon-button-dark';
|
|
492
|
+
wrap.style.cursor = 'pointer';
|
|
493
|
+
|
|
494
|
+
// Handle click directly on the wrapper element
|
|
495
|
+
wrap.addEventListener('click', (e) => {
|
|
496
|
+
e.preventDefault();
|
|
497
|
+
e.stopPropagation();
|
|
498
|
+
setAIChatMessage('Implement the TODO');
|
|
499
|
+
setIsAIChatOpen(true);
|
|
500
|
+
});
|
|
501
|
+
|
|
488
502
|
const reactContainer =
|
|
489
503
|
document.createElement('div');
|
|
490
504
|
wrap.appendChild(reactContainer);
|
|
491
505
|
|
|
492
506
|
const root = createRoot(reactContainer);
|
|
493
507
|
root.render(
|
|
494
|
-
<
|
|
495
|
-
onClick={() => {
|
|
496
|
-
setIsAIChatOpen(true);
|
|
497
|
-
// setAIChatMessage('Implement the TODO');
|
|
498
|
-
handleSendMessage('Implement the TODO');
|
|
499
|
-
}}
|
|
500
|
-
>
|
|
501
|
-
<SparklesSVG width={14} height={14} />
|
|
502
|
-
</div>,
|
|
508
|
+
<SparklesSVG width={14} height={14} />,
|
|
503
509
|
);
|
|
510
|
+
|
|
504
511
|
return wrap;
|
|
505
512
|
}
|
|
506
513
|
|
|
@@ -43,7 +43,7 @@ export const CodeEditor = ({
|
|
|
43
43
|
codeEditorRef,
|
|
44
44
|
enableAutoFollow,
|
|
45
45
|
setIsAIChatOpen,
|
|
46
|
-
|
|
46
|
+
setAIChatMessage,
|
|
47
47
|
} = useContext(VZCodeContext);
|
|
48
48
|
|
|
49
49
|
// Set `doc.data.isInteracting` to `true` when the user is interacting
|
|
@@ -117,7 +117,7 @@ export const CodeEditor = ({
|
|
|
117
117
|
aiCopilotEndpoint,
|
|
118
118
|
esLintSource,
|
|
119
119
|
setIsAIChatOpen,
|
|
120
|
-
|
|
120
|
+
setAIChatMessage,
|
|
121
121
|
});
|
|
122
122
|
|
|
123
123
|
if (isMounted) {
|
|
@@ -142,7 +142,7 @@ export const CodeEditor = ({
|
|
|
142
142
|
aiCopilotEndpoint,
|
|
143
143
|
esLintSource,
|
|
144
144
|
setIsAIChatOpen,
|
|
145
|
-
|
|
145
|
+
setAIChatMessage,
|
|
146
146
|
]);
|
|
147
147
|
|
|
148
148
|
// Every time the active file switches from one file to another,
|
|
@@ -362,6 +362,25 @@
|
|
|
362
362
|
margin-top: 1rem;
|
|
363
363
|
margin-bottom: 0.5rem;
|
|
364
364
|
color: inherit;
|
|
365
|
+
font-weight: 600;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
h1 {
|
|
369
|
+
font-size: 1.25rem; // 20px at 16px base
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
h2 {
|
|
373
|
+
font-size: 1.125rem; // 18px at 16px base
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
h3 {
|
|
377
|
+
font-size: 1rem; // 16px at 16px base
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
h4,
|
|
381
|
+
h5,
|
|
382
|
+
h6 {
|
|
383
|
+
font-size: 0.875rem; // 14px at 16px base
|
|
365
384
|
}
|
|
366
385
|
|
|
367
386
|
ul,
|
|
@@ -784,10 +803,29 @@
|
|
|
784
803
|
h6 {
|
|
785
804
|
margin: 8px 0 6px 0;
|
|
786
805
|
color: var(--vh-color-neutral-04);
|
|
806
|
+
font-weight: 600;
|
|
787
807
|
|
|
788
808
|
&:first-child {
|
|
789
809
|
margin-top: 0;
|
|
790
810
|
}
|
|
791
811
|
}
|
|
812
|
+
|
|
813
|
+
h1 {
|
|
814
|
+
font-size: 1.125rem; // 18px at 16px base
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
h2 {
|
|
818
|
+
font-size: 1rem; // 16px at 16px base
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
h3 {
|
|
822
|
+
font-size: 0.9375rem; // 15px at 16px base
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
h4,
|
|
826
|
+
h5,
|
|
827
|
+
h6 {
|
|
828
|
+
font-size: 0.8125rem; // 13px at 16px base
|
|
829
|
+
}
|
|
792
830
|
}
|
|
793
831
|
}
|
|
@@ -32,7 +32,7 @@ import { MicSVG } from '../Icons/MicSVG';
|
|
|
32
32
|
import { sortFileTree } from '../sortFileTree';
|
|
33
33
|
import { SplitPaneResizeContext } from '../SplitPaneResizeContext';
|
|
34
34
|
import { VZCodeContext } from '../VZCodeContext';
|
|
35
|
-
import { AIChat } from '
|
|
35
|
+
import { AIChat } from '../../llm-streaming-ui/components/index.js';
|
|
36
36
|
import { Listing } from './Listing';
|
|
37
37
|
import { Search } from './Search';
|
|
38
38
|
import { DeleteConfirmationModal } from './DeleteConfirmationModal';
|