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
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
display: flex;
|
|
12
12
|
align-items: center;
|
|
13
13
|
gap: 8px;
|
|
14
|
-
font-size:
|
|
14
|
+
font-size: 14px;
|
|
15
15
|
color: var(--bs-secondary-color);
|
|
16
16
|
|
|
17
17
|
.files-changed {
|
|
@@ -134,4 +134,40 @@
|
|
|
134
134
|
background-color: var(--d2h-bg-color);
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
|
+
|
|
138
|
+
.deleted-file {
|
|
139
|
+
margin-bottom: 8px;
|
|
140
|
+
border: 1px solid #30363d;
|
|
141
|
+
border-radius: 6px;
|
|
142
|
+
background-color: rgb(41, 44, 52);
|
|
143
|
+
overflow: hidden;
|
|
144
|
+
|
|
145
|
+
.deleted-file-header {
|
|
146
|
+
display: flex;
|
|
147
|
+
justify-content: space-between;
|
|
148
|
+
align-items: center;
|
|
149
|
+
padding: 12px 16px;
|
|
150
|
+
background-color: #161b22;
|
|
151
|
+
border-bottom: 1px solid #30363d;
|
|
152
|
+
|
|
153
|
+
.deleted-file-name {
|
|
154
|
+
color: #e6edf3;
|
|
155
|
+
font-weight: 600;
|
|
156
|
+
font-family:
|
|
157
|
+
'SFMono-Regular', Consolas, 'Liberation Mono',
|
|
158
|
+
Menlo, monospace;
|
|
159
|
+
font-size: 14px;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.deleted-file-status {
|
|
163
|
+
color: #f85149;
|
|
164
|
+
font-weight: 600;
|
|
165
|
+
font-size: 14px;
|
|
166
|
+
background-color: rgba(248, 81, 73, 0.1);
|
|
167
|
+
padding: 4px 8px;
|
|
168
|
+
border-radius: 4px;
|
|
169
|
+
border: 1px solid rgba(248, 81, 73, 0.4);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
137
173
|
}
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
UnifiedFilesDiff,
|
|
10
10
|
parseUnifiedDiffStats,
|
|
11
11
|
combineUnifiedDiffs,
|
|
12
|
+
isFileDeletion,
|
|
13
|
+
getDeletedFileName,
|
|
12
14
|
} from '../../../utils/fileDiff';
|
|
13
15
|
import * as Diff2Html from 'diff2html';
|
|
14
16
|
import 'diff2html/bundles/css/diff2html.min.css';
|
|
@@ -41,21 +43,33 @@ export const DiffView = forwardRef<
|
|
|
41
43
|
useContext(VZCodeContext);
|
|
42
44
|
const diffContainerRef = useRef<HTMLDivElement>(null);
|
|
43
45
|
|
|
44
|
-
const
|
|
46
|
+
const allDiffs = Object.values(diffData).filter(
|
|
45
47
|
(diff) => diff.length > 0,
|
|
46
48
|
);
|
|
47
49
|
|
|
48
|
-
//
|
|
50
|
+
// Separate deleted files from regular diffs
|
|
51
|
+
const deletedFiles: string[] = [];
|
|
52
|
+
const regularDiffs: string[] = [];
|
|
53
|
+
|
|
54
|
+
for (const diff of allDiffs) {
|
|
55
|
+
if (isFileDeletion(diff)) {
|
|
56
|
+
deletedFiles.push(diff);
|
|
57
|
+
} else {
|
|
58
|
+
regularDiffs.push(diff);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Calculate statistics from regular unified diffs only
|
|
49
63
|
let totalAdditions = 0;
|
|
50
64
|
let totalDeletions = 0;
|
|
51
65
|
|
|
52
|
-
for (const unifiedDiff of
|
|
66
|
+
for (const unifiedDiff of regularDiffs) {
|
|
53
67
|
const stats = parseUnifiedDiffStats(unifiedDiff);
|
|
54
68
|
totalAdditions += stats.additions;
|
|
55
69
|
totalDeletions += stats.deletions;
|
|
56
70
|
}
|
|
57
71
|
|
|
58
|
-
// Combine
|
|
72
|
+
// Combine regular unified diffs and convert to HTML using diff2html
|
|
59
73
|
const combinedUnifiedDiff = combineUnifiedDiffs(diffData);
|
|
60
74
|
const diffHtml = Diff2Html.html(combinedUnifiedDiff, {
|
|
61
75
|
drawFileList: false,
|
|
@@ -155,7 +169,7 @@ export const DiffView = forwardRef<
|
|
|
155
169
|
};
|
|
156
170
|
}, [diffHtml, content, openTab, setIsAIChatOpen]);
|
|
157
171
|
|
|
158
|
-
if (
|
|
172
|
+
if (allDiffs.length === 0) {
|
|
159
173
|
return null;
|
|
160
174
|
}
|
|
161
175
|
|
|
@@ -164,8 +178,8 @@ export const DiffView = forwardRef<
|
|
|
164
178
|
<div className="diff-summary" id="diff-summary">
|
|
165
179
|
<div className="diff-stats">
|
|
166
180
|
<span className="files-changed">
|
|
167
|
-
{
|
|
168
|
-
{
|
|
181
|
+
{allDiffs.length} file
|
|
182
|
+
{allDiffs.length !== 1 ? 's' : ''} changed
|
|
169
183
|
</span>
|
|
170
184
|
{totalAdditions > 0 && (
|
|
171
185
|
<span className="additions">
|
|
@@ -177,18 +191,43 @@ export const DiffView = forwardRef<
|
|
|
177
191
|
-{totalDeletions}
|
|
178
192
|
</span>
|
|
179
193
|
)}
|
|
194
|
+
{deletedFiles.length > 0 && (
|
|
195
|
+
<span className="deletions">
|
|
196
|
+
{deletedFiles.length} deleted
|
|
197
|
+
</span>
|
|
198
|
+
)}
|
|
180
199
|
</div>
|
|
181
200
|
</div>
|
|
182
201
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
202
|
+
{/* Render deleted files */}
|
|
203
|
+
{deletedFiles.map((deletionMarker, index) => {
|
|
204
|
+
const fileName = getDeletedFileName(deletionMarker);
|
|
205
|
+
return (
|
|
206
|
+
<div key={index} className="deleted-file">
|
|
207
|
+
<div className="deleted-file-header">
|
|
208
|
+
<span className="deleted-file-name">
|
|
209
|
+
{fileName}
|
|
210
|
+
</span>
|
|
211
|
+
<span className="deleted-file-status">
|
|
212
|
+
File deleted
|
|
213
|
+
</span>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
);
|
|
217
|
+
})}
|
|
218
|
+
|
|
219
|
+
{/* Render regular diffs */}
|
|
220
|
+
{regularDiffs.length > 0 && (
|
|
221
|
+
<div
|
|
222
|
+
className="diff-files"
|
|
223
|
+
ref={diffContainerRef}
|
|
224
|
+
tabIndex={-1}
|
|
225
|
+
role="region"
|
|
226
|
+
aria-label="Code diff content"
|
|
227
|
+
aria-describedby="diff-summary"
|
|
228
|
+
dangerouslySetInnerHTML={{ __html: diffHtml }}
|
|
229
|
+
/>
|
|
230
|
+
)}
|
|
192
231
|
</div>
|
|
193
232
|
);
|
|
194
233
|
});
|
|
@@ -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';
|
|
@@ -9,3 +9,10 @@ export const enableAskMode = false;
|
|
|
9
9
|
|
|
10
10
|
// Phase 0: Feature flag for minimal AI edit flow
|
|
11
11
|
export const enableMinimalEditFlow = true;
|
|
12
|
+
|
|
13
|
+
// If true, only include the minimal set of extensions,
|
|
14
|
+
// namely only the JSON1 OT extension and that's it.
|
|
15
|
+
// This is useful for testing and debugging the OT functionality
|
|
16
|
+
// without any other extensions getting in the way,
|
|
17
|
+
// just to rule them out as the source of a bug.
|
|
18
|
+
export const MINIMAL_EXTENSIONS = false;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# LLM Streaming Server
|
|
2
|
+
|
|
3
|
+
Server-side library for streaming LLM responses with ShareDB integration.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This module is being extracted from VZCode's AI chat functionality to become a standalone, reusable library. It provides server-side functionality for:
|
|
8
|
+
|
|
9
|
+
- **LLM Streaming**: Creating and managing streaming LLM functions with OpenAI/OpenRouter
|
|
10
|
+
- **AI Code Editing**: Performing AI-assisted code transformations and edits
|
|
11
|
+
- **ShareDB Integration**: Managing chat operations and real-time collaboration
|
|
12
|
+
- **Request Validation**: Validating incoming AI chat requests
|
|
13
|
+
- **Error Handling**: Handling errors gracefully in streaming contexts
|
|
14
|
+
|
|
15
|
+
## Current Status
|
|
16
|
+
|
|
17
|
+
⚠️ **Work in Progress**: This is Phase 1 of the extraction plan. The files have been copied to this directory and imports have been updated, but the code is not yet decoupled from VZCode.
|
|
18
|
+
|
|
19
|
+
## Files
|
|
20
|
+
|
|
21
|
+
- `index.ts` - Main entry point exporting all public APIs
|
|
22
|
+
- `llmStreaming.ts` - Core LLM streaming functionality with OpenAI/OpenRouter
|
|
23
|
+
- `aiEditing.ts` - AI-assisted code editing operations
|
|
24
|
+
- `chatOperations.ts` - ShareDB chat operations (messages, events, streaming)
|
|
25
|
+
- `validation.ts` - Request validation utilities
|
|
26
|
+
- `errorHandling.ts` - Error handling for streaming contexts
|
|
27
|
+
|
|
28
|
+
## Dependencies
|
|
29
|
+
|
|
30
|
+
Currently depends on:
|
|
31
|
+
|
|
32
|
+
- `openai` - OpenAI SDK for LLM interactions
|
|
33
|
+
- `sharedb` - Real-time collaborative editing framework
|
|
34
|
+
- `llm-code-format` - Markdown parsing for code blocks
|
|
35
|
+
- `editcodewithai` - Code editing utilities
|
|
36
|
+
- VZCode utilities: `../types.js`, `../ot.js`, `../randomId.js`, etc.
|
|
37
|
+
|
|
38
|
+
## Future Plans
|
|
39
|
+
|
|
40
|
+
This module will eventually be:
|
|
41
|
+
|
|
42
|
+
1. Decoupled from VZCode-specific types and utilities
|
|
43
|
+
2. Published as `@vizhub/llm-streaming-server` on npm
|
|
44
|
+
3. Made framework-agnostic (can work with Express, Fastify, etc.)
|
|
45
|
+
4. Fully documented with usage examples
|
|
46
|
+
|
|
47
|
+
See the [main issue](https://github.com/vizhub-core/vzcode/issues/XXX) for the full extraction plan.
|
|
48
|
+
|
|
49
|
+
## Usage (Current)
|
|
50
|
+
|
|
51
|
+
Currently, this module is imported by `src/server/aiChatHandler/index.ts`:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { createLLMFunction } from '../../llm-streaming-server/llmStreaming.js';
|
|
55
|
+
import { performAIEditing } from '../../llm-streaming-server/aiEditing.js';
|
|
56
|
+
import {
|
|
57
|
+
ensureChatsExist,
|
|
58
|
+
ensureChatExists,
|
|
59
|
+
addUserMessage,
|
|
60
|
+
setAIStatus,
|
|
61
|
+
} from '../../llm-streaming-server/chatOperations.js';
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Contributing
|
|
65
|
+
|
|
66
|
+
As this module is being extracted, please:
|
|
67
|
+
|
|
68
|
+
- Keep changes minimal and focused
|
|
69
|
+
- Maintain backward compatibility with VZCode
|
|
70
|
+
- Update tests when modifying functionality
|
|
71
|
+
- Document any new dependencies or breaking changes
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assembleFullPrompt,
|
|
3
|
+
prepareFilesForPrompt,
|
|
4
|
+
} from 'editcodewithai';
|
|
5
|
+
import { formatMarkdownFiles } from 'llm-code-format';
|
|
6
|
+
import {
|
|
7
|
+
createFilesSnapshot,
|
|
8
|
+
generateFilesUnifiedDiff,
|
|
9
|
+
} from '../utils/fileDiff.js';
|
|
10
|
+
|
|
11
|
+
// Dev flag for waiting 1 second before starting the LLM function.
|
|
12
|
+
// Useful for debugging and testing purposes, e.g. checking the typing indicator.
|
|
13
|
+
const delayStart = false;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Performs AI chat without editing - just generates a response
|
|
17
|
+
*/
|
|
18
|
+
export const performAIChat = async ({
|
|
19
|
+
prompt,
|
|
20
|
+
shareDBDoc,
|
|
21
|
+
llmFunction,
|
|
22
|
+
}) => {
|
|
23
|
+
const { files } = prepareFilesForPrompt(
|
|
24
|
+
shareDBDoc.data.files,
|
|
25
|
+
);
|
|
26
|
+
const filesContext = formatMarkdownFiles(files);
|
|
27
|
+
|
|
28
|
+
// 2. Assemble the final prompt for Q&A mode
|
|
29
|
+
const fullPrompt = assembleFullPrompt({
|
|
30
|
+
filesContext,
|
|
31
|
+
prompt,
|
|
32
|
+
editFormat: 'whole',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (delayStart) {
|
|
36
|
+
await new Promise((resolve) =>
|
|
37
|
+
setTimeout(resolve, 1000),
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Call the LLM function which will handle streaming but won't edit files
|
|
42
|
+
const result = await llmFunction(fullPrompt);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
content: result.content,
|
|
46
|
+
generationId: result.generationId,
|
|
47
|
+
diffData: {}, // No file changes in ask mode
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Performs AI editing operations using streaming with incremental OT operations
|
|
53
|
+
*/
|
|
54
|
+
export const performAIEditing = async ({
|
|
55
|
+
prompt,
|
|
56
|
+
shareDBDoc,
|
|
57
|
+
llmFunction,
|
|
58
|
+
runCode,
|
|
59
|
+
}) => {
|
|
60
|
+
// Capture the current state of files before editing
|
|
61
|
+
const beforeFiles = createFilesSnapshot(
|
|
62
|
+
shareDBDoc.data.files,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const { files } = prepareFilesForPrompt(
|
|
66
|
+
shareDBDoc.data.files,
|
|
67
|
+
);
|
|
68
|
+
const filesContext = formatMarkdownFiles(files);
|
|
69
|
+
|
|
70
|
+
// Assemble the final prompt
|
|
71
|
+
const fullPrompt = assembleFullPrompt({
|
|
72
|
+
filesContext,
|
|
73
|
+
prompt,
|
|
74
|
+
editFormat: 'whole',
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (delayStart) {
|
|
78
|
+
await new Promise((resolve) =>
|
|
79
|
+
setTimeout(resolve, 1000),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Call the LLM function which will handle streaming, incremental file updates, and Prettier formatting
|
|
84
|
+
const result = await llmFunction(fullPrompt);
|
|
85
|
+
|
|
86
|
+
runCode();
|
|
87
|
+
|
|
88
|
+
// 4. Capture the state of files after editing and formatting, then generate diff
|
|
89
|
+
const afterFiles = createFilesSnapshot(
|
|
90
|
+
shareDBDoc.data.files,
|
|
91
|
+
);
|
|
92
|
+
const diffData = generateFilesUnifiedDiff(
|
|
93
|
+
beforeFiles,
|
|
94
|
+
afterFiles,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
content: result.content,
|
|
99
|
+
generationId: result.generationId,
|
|
100
|
+
diffData,
|
|
101
|
+
};
|
|
102
|
+
};
|