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
|
@@ -67,8 +67,10 @@ import {
|
|
|
67
67
|
} from '@valtown/codemirror-ts';
|
|
68
68
|
import { getFileExtension } from '../utils/fileExtension';
|
|
69
69
|
import { SparklesSVG } from '../Icons/SparklesSVG';
|
|
70
|
+
import { MINIMAL_EXTENSIONS } from '../featureFlags';
|
|
70
71
|
|
|
71
72
|
const DEBUG = false;
|
|
73
|
+
const enableToDoPlugin = true;
|
|
72
74
|
|
|
73
75
|
// Define a StateField to store the file name.
|
|
74
76
|
// This should be defined at the module level if it's to be imported by other modules.
|
|
@@ -136,6 +138,7 @@ const languageExtensions = {
|
|
|
136
138
|
html: () => html(htmlConfig),
|
|
137
139
|
css,
|
|
138
140
|
md: markdown,
|
|
141
|
+
prompt: markdown,
|
|
139
142
|
svelte,
|
|
140
143
|
};
|
|
141
144
|
|
|
@@ -186,7 +189,7 @@ export const getOrCreateEditor = async ({
|
|
|
186
189
|
esLintSource,
|
|
187
190
|
rainbowBracketsEnabled = true,
|
|
188
191
|
setIsAIChatOpen,
|
|
189
|
-
|
|
192
|
+
setAIChatMessage,
|
|
190
193
|
}: {
|
|
191
194
|
// TODO pass this in from the outside
|
|
192
195
|
paneId?: PaneId;
|
|
@@ -223,7 +226,7 @@ export const getOrCreateEditor = async ({
|
|
|
223
226
|
) => Promise<readonly Diagnostic[]>;
|
|
224
227
|
rainbowBracketsEnabled?: boolean; // New parameter type
|
|
225
228
|
setIsAIChatOpen: (isAIChatOpen: boolean) => void;
|
|
226
|
-
|
|
229
|
+
setAIChatMessage: (message: string) => void;
|
|
227
230
|
}): Promise<ExtendedEditorCacheValue> => {
|
|
228
231
|
// Cache hit
|
|
229
232
|
|
|
@@ -253,8 +256,10 @@ export const getOrCreateEditor = async ({
|
|
|
253
256
|
// Create a compartment for rainbow brackets so that it can be enabled/disabled dynamically.
|
|
254
257
|
const rainbowBracketsCompartment = new Compartment();
|
|
255
258
|
|
|
259
|
+
// Create a compartment for language so that it can be changed dynamically.
|
|
260
|
+
const languageCompartment = new Compartment();
|
|
261
|
+
|
|
256
262
|
// The CodeMirror extensions to use.
|
|
257
|
-
// const extensions = [autocompletion(), html(htmlConfig)]
|
|
258
263
|
const extensions = [];
|
|
259
264
|
|
|
260
265
|
// Initialize the fileNameStateField with the actual file name
|
|
@@ -273,26 +278,29 @@ export const getOrCreateEditor = async ({
|
|
|
273
278
|
}),
|
|
274
279
|
);
|
|
275
280
|
|
|
276
|
-
//
|
|
277
|
-
if (
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
281
|
+
// Only add presence extensions if MINIMAL_EXTENSIONS is false
|
|
282
|
+
if (!MINIMAL_EXTENSIONS) {
|
|
283
|
+
// Deals with broadcasting changes in cursor location and selection.
|
|
284
|
+
if (localPresence) {
|
|
285
|
+
extensions.push(
|
|
286
|
+
json1PresenceBroadcast({
|
|
287
|
+
path: textPath,
|
|
288
|
+
localPresence,
|
|
289
|
+
usernameRef,
|
|
290
|
+
}),
|
|
291
|
+
);
|
|
292
|
+
}
|
|
286
293
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
294
|
+
// Deals with receiving the broadcast from other clients and displaying them.
|
|
295
|
+
if (docPresence) {
|
|
296
|
+
extensions.push(
|
|
297
|
+
json1PresenceDisplay({
|
|
298
|
+
path: textPath,
|
|
299
|
+
docPresence,
|
|
300
|
+
enableAutoFollowRef,
|
|
301
|
+
}),
|
|
302
|
+
);
|
|
303
|
+
}
|
|
296
304
|
}
|
|
297
305
|
} else {
|
|
298
306
|
// If the ShareDB document is not provided,
|
|
@@ -300,44 +308,7 @@ export const getOrCreateEditor = async ({
|
|
|
300
308
|
extensions.push(EditorView.editable.of(false));
|
|
301
309
|
}
|
|
302
310
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
// This is the "basic setup" for CodeMirror,
|
|
306
|
-
// which actually adds a ton on functionality.
|
|
307
|
-
// TODO vet this functionality, and determine how much
|
|
308
|
-
// we want to replace with
|
|
309
|
-
// https://github.com/vizhub-core/vzcode/issues/134
|
|
310
|
-
extensions.push(basicSetup);
|
|
311
|
-
|
|
312
|
-
if (esLintSource) {
|
|
313
|
-
extensions.push(lintGutter()); // Show lint icons in the gutter
|
|
314
|
-
extensions.push(
|
|
315
|
-
linter(esLintSource, {
|
|
316
|
-
// You can configure linter options here, e.g., delay
|
|
317
|
-
delay: 750,
|
|
318
|
-
}),
|
|
319
|
-
);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// This supports dynamic changing of the theme.
|
|
323
|
-
extensions.push(
|
|
324
|
-
themeCompartment.of(themeOptionsByLabel[theme].value),
|
|
325
|
-
);
|
|
326
|
-
|
|
327
|
-
// Adds compartment for rainbow brackets with initial toggle state.
|
|
328
|
-
extensions.push(
|
|
329
|
-
rainbowBracketsCompartment.of(
|
|
330
|
-
rainbowBracketsEnabled ? rainbowBrackets() : [],
|
|
331
|
-
),
|
|
332
|
-
);
|
|
333
|
-
|
|
334
|
-
// TODO handle dynamic changing of the file extension.
|
|
335
|
-
// TODO handle dynamic file extensions by making
|
|
336
|
-
// the CodeMirror language extension dynamic
|
|
337
|
-
// using a Compartment.
|
|
338
|
-
const languageCompartment = new Compartment();
|
|
339
|
-
// See https://github.com/vizhub-core/vzcode/issues/55
|
|
340
|
-
|
|
311
|
+
// Add language extension (needed for both minimal and full modes)
|
|
341
312
|
const languageExtension =
|
|
342
313
|
getLanguageExtension(fileExtension);
|
|
343
314
|
if (languageExtension) {
|
|
@@ -345,232 +316,275 @@ export const getOrCreateEditor = async ({
|
|
|
345
316
|
languageCompartment.of(languageExtension),
|
|
346
317
|
);
|
|
347
318
|
} else {
|
|
348
|
-
// Not sure if this case even works.
|
|
349
|
-
// TODO manually test this case by creating a file
|
|
350
|
-
// that has no extension, opening it up,
|
|
351
|
-
// and then adding an extension.
|
|
352
|
-
// console.warn(
|
|
353
|
-
// `No language extension for file extension: ${fileExtension}`,
|
|
354
|
-
// );
|
|
355
|
-
// We still need to push the compartment,
|
|
356
|
-
// otherwise the compartment won't work when
|
|
357
|
-
// a file extension _is_ added later on.
|
|
358
319
|
extensions.push(languageCompartment.of([]));
|
|
359
320
|
}
|
|
360
321
|
|
|
361
|
-
//
|
|
362
|
-
if (
|
|
363
|
-
extensions
|
|
364
|
-
|
|
322
|
+
// If MINIMAL_EXTENSIONS is true, only include the JSON1 OT extension and essential functionality
|
|
323
|
+
if (MINIMAL_EXTENSIONS) {
|
|
324
|
+
// Only the most basic extensions are added
|
|
325
|
+
// The JSON1 OT extension and language extension are already added above
|
|
326
|
+
} else {
|
|
327
|
+
// Full feature set - include all extensions
|
|
328
|
+
extensions.push(colorsInTextPlugin);
|
|
329
|
+
|
|
330
|
+
// This is the "basic setup" for CodeMirror,
|
|
331
|
+
// which actually adds a ton on functionality.
|
|
332
|
+
// TODO vet this functionality, and determine how much
|
|
333
|
+
// we want to replace with
|
|
334
|
+
// https://github.com/vizhub-core/vzcode/issues/134
|
|
335
|
+
extensions.push(basicSetup);
|
|
336
|
+
|
|
337
|
+
if (esLintSource) {
|
|
338
|
+
extensions.push(lintGutter()); // Show lint icons in the gutter
|
|
339
|
+
extensions.push(
|
|
340
|
+
linter(esLintSource, {
|
|
341
|
+
// You can configure linter options here, e.g., delay
|
|
342
|
+
delay: 750,
|
|
343
|
+
}),
|
|
344
|
+
);
|
|
345
|
+
}
|
|
365
346
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
// for hot reloading.
|
|
371
|
-
// TODO consider leveraging the new `dragEnd` event handler.
|
|
372
|
-
// and removing the `onInteract` callback, replacing it with
|
|
373
|
-
// `onInteractStart` and `onInteractEnd`.
|
|
374
|
-
// That may be tricky for one-off interactions though, like
|
|
375
|
-
// the boolean checkboxes. The color pickers are also tricky,
|
|
376
|
-
// as they would also need to be able to handle `onInteractEnd`.
|
|
377
|
-
// See https://github.com/replit/codemirror-interact/issues/14
|
|
378
|
-
extensions.push(
|
|
379
|
-
widgets({ onInteract, customInteractRules }),
|
|
380
|
-
);
|
|
347
|
+
// This supports dynamic changing of the theme.
|
|
348
|
+
extensions.push(
|
|
349
|
+
themeCompartment.of(themeOptionsByLabel[theme].value),
|
|
350
|
+
);
|
|
381
351
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
// extensions.push(
|
|
389
|
-
// AIAssistCodeMirrorKeyMap({
|
|
390
|
-
// shareDBDoc,
|
|
391
|
-
// fileId,
|
|
392
|
-
// tabList,
|
|
393
|
-
// aiAssistEndpoint,
|
|
394
|
-
// aiAssistOptions,
|
|
395
|
-
// }),
|
|
396
|
-
// );
|
|
397
|
-
|
|
398
|
-
// Add the extension that provides indentation markers.
|
|
399
|
-
extensions.push(
|
|
400
|
-
indentationMarkers({
|
|
401
|
-
// thickness: 2,
|
|
402
|
-
colors: {
|
|
403
|
-
light: '#4d586b',
|
|
404
|
-
dark: '#4d586b',
|
|
405
|
-
activeLight: '#8e949f',
|
|
406
|
-
activeDark: '#8e949f',
|
|
407
|
-
},
|
|
408
|
-
}),
|
|
409
|
-
);
|
|
352
|
+
// Adds compartment for rainbow brackets with initial toggle state.
|
|
353
|
+
extensions.push(
|
|
354
|
+
rainbowBracketsCompartment.of(
|
|
355
|
+
rainbowBracketsEnabled ? rainbowBrackets() : [],
|
|
356
|
+
),
|
|
357
|
+
);
|
|
410
358
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
359
|
+
// Enable line wrapping for Markdown files
|
|
360
|
+
if (
|
|
361
|
+
fileExtension === 'md' ||
|
|
362
|
+
fileExtension === 'prompt'
|
|
363
|
+
) {
|
|
364
|
+
extensions.push(EditorView.lineWrapping);
|
|
365
|
+
}
|
|
414
366
|
|
|
367
|
+
// Add interactive widgets.
|
|
368
|
+
// Includes the Alt+drag functionality for numbers.
|
|
369
|
+
// Calls `onInteract` when one of those widgets is interacted with.
|
|
370
|
+
// This can be used to trigger a transition to throttled mode
|
|
371
|
+
// for hot reloading.
|
|
372
|
+
// TODO consider leveraging the new `dragEnd` event handler.
|
|
373
|
+
// and removing the `onInteract` callback, replacing it with
|
|
374
|
+
// `onInteractStart` and `onInteractEnd`.
|
|
375
|
+
// That may be tricky for one-off interactions though, like
|
|
376
|
+
// the boolean checkboxes. The color pickers are also tricky,
|
|
377
|
+
// as they would also need to be able to handle `onInteractEnd`.
|
|
378
|
+
// See https://github.com/replit/codemirror-interact/issues/14
|
|
415
379
|
extensions.push(
|
|
416
|
-
|
|
417
|
-
tsFacetWorker.of({ worker: tsWorker, path: name }),
|
|
418
|
-
tsSyncWorker(),
|
|
419
|
-
tsLinterWorker(),
|
|
420
|
-
autocompletion({
|
|
421
|
-
override: [tsAutocompleteWorker()],
|
|
422
|
-
}),
|
|
423
|
-
tsHoverWorker(),
|
|
424
|
-
],
|
|
380
|
+
widgets({ onInteract, customInteractRules }),
|
|
425
381
|
);
|
|
426
|
-
}
|
|
427
382
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
// extensions.push(
|
|
432
|
-
// showMinimap.compute(['doc'], () => ({
|
|
433
|
-
// create: () => ({
|
|
434
|
-
// dom: document.createElement('div'),
|
|
435
|
-
// }),
|
|
436
|
-
// // Without this, performance is terrible.
|
|
437
|
-
// displayText: 'blocks',
|
|
438
|
-
// })),
|
|
439
|
-
// );
|
|
440
|
-
|
|
441
|
-
// VSCode keybindings
|
|
442
|
-
// See https://github.com/replit/codemirror-vscode-keymap#usage
|
|
443
|
-
// extensions.push(keymap.of(vscodeKeymap));
|
|
444
|
-
extensions.push(
|
|
445
|
-
keymap.of(
|
|
446
|
-
vscodeKeymap.map((binding) => {
|
|
447
|
-
// Here we override the Shift+Enter behavior specifically,
|
|
448
|
-
// as that can be used to trigger a manual save/Prettier,
|
|
449
|
-
// and the default behavior from the keymap interferes.
|
|
450
|
-
if (binding.key === 'Enter') {
|
|
451
|
-
delete binding.shift;
|
|
452
|
-
}
|
|
453
|
-
return binding;
|
|
454
|
-
}),
|
|
455
|
-
),
|
|
456
|
-
);
|
|
383
|
+
// TODO fix the bugginess in this one where
|
|
384
|
+
// the highlight persists after the mouse leaves.
|
|
385
|
+
// extensions.push(highlightWidgets);
|
|
457
386
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
387
|
+
extensions.push(rotationIndicator);
|
|
388
|
+
|
|
389
|
+
// extensions.push(
|
|
390
|
+
// AIAssistCodeMirrorKeyMap({
|
|
391
|
+
// shareDBDoc,
|
|
392
|
+
// fileId,
|
|
393
|
+
// tabList,
|
|
394
|
+
// aiAssistEndpoint,
|
|
395
|
+
// aiAssistOptions,
|
|
396
|
+
// }),
|
|
397
|
+
// );
|
|
398
|
+
|
|
399
|
+
// Add the extension that provides indentation markers.
|
|
400
|
+
extensions.push(
|
|
401
|
+
indentationMarkers({
|
|
402
|
+
// thickness: 2,
|
|
403
|
+
colors: {
|
|
404
|
+
light: '#4d586b',
|
|
405
|
+
dark: '#4d586b',
|
|
406
|
+
activeLight: '#8e949f',
|
|
407
|
+
activeDark: '#8e949f',
|
|
408
|
+
},
|
|
409
|
+
}),
|
|
463
410
|
);
|
|
464
|
-
if (aiCopilotEndpoint) {
|
|
465
|
-
extensions.push(copilot({ aiCopilotEndpoint }));
|
|
466
|
-
}
|
|
467
411
|
|
|
468
|
-
|
|
412
|
+
if (name.endsWith('ts') || name.endsWith('tsx')) {
|
|
413
|
+
// Initialize worker if needed
|
|
414
|
+
const tsWorker = await initializeWorker();
|
|
469
415
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
416
|
+
extensions.push(
|
|
417
|
+
...[
|
|
418
|
+
tsFacetWorker.of({
|
|
419
|
+
worker: tsWorker,
|
|
420
|
+
path: name,
|
|
421
|
+
}),
|
|
422
|
+
tsSyncWorker(),
|
|
423
|
+
tsLinterWorker(),
|
|
424
|
+
autocompletion({
|
|
425
|
+
override: [tsAutocompleteWorker()],
|
|
426
|
+
}),
|
|
427
|
+
tsHoverWorker(),
|
|
428
|
+
],
|
|
429
|
+
);
|
|
430
|
+
}
|
|
477
431
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
432
|
+
// Show the minimap
|
|
433
|
+
// See https://github.com/replit/codemirror-minimap#usage
|
|
434
|
+
// This extension has poor performance, so it's disabled for now.
|
|
435
|
+
// extensions.push(
|
|
436
|
+
// showMinimap.compute(['doc'], () => ({
|
|
437
|
+
// create: () => ({
|
|
438
|
+
// dom: document.createElement('div'),
|
|
439
|
+
// }),
|
|
440
|
+
// // Without this, performance is terrible.
|
|
441
|
+
// displayText: 'blocks',
|
|
442
|
+
// })),
|
|
443
|
+
// );
|
|
481
444
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
handleSendMessage('Implement the TODO');
|
|
499
|
-
}}
|
|
500
|
-
>
|
|
501
|
-
<SparklesSVG width={14} height={14} />
|
|
502
|
-
</div>,
|
|
503
|
-
);
|
|
504
|
-
return wrap;
|
|
505
|
-
}
|
|
445
|
+
// VSCode keybindings
|
|
446
|
+
// See https://github.com/replit/codemirror-vscode-keymap#usage
|
|
447
|
+
// extensions.push(keymap.of(vscodeKeymap));
|
|
448
|
+
extensions.push(
|
|
449
|
+
keymap.of(
|
|
450
|
+
vscodeKeymap.map((binding) => {
|
|
451
|
+
// Here we override the Shift+Enter behavior specifically,
|
|
452
|
+
// as that can be used to trigger a manual save/Prettier,
|
|
453
|
+
// and the default behavior from the keymap interferes.
|
|
454
|
+
if (binding.key === 'Enter') {
|
|
455
|
+
delete binding.shift;
|
|
456
|
+
}
|
|
457
|
+
return binding;
|
|
458
|
+
}),
|
|
459
|
+
),
|
|
460
|
+
);
|
|
506
461
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
462
|
+
// Adds copilot completions
|
|
463
|
+
DEBUG &&
|
|
464
|
+
console.log(
|
|
465
|
+
'[getOrCreateEditor] aiCopilotEndpoint: ',
|
|
466
|
+
aiCopilotEndpoint,
|
|
467
|
+
);
|
|
468
|
+
if (aiCopilotEndpoint) {
|
|
469
|
+
extensions.push(copilot({ aiCopilotEndpoint }));
|
|
510
470
|
}
|
|
511
471
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
while (!findToDo.next().done) {
|
|
521
|
-
const { from, to } = findToDo.value;
|
|
522
|
-
const node = tree.resolve(from);
|
|
523
|
-
let inComment = false;
|
|
524
|
-
|
|
525
|
-
// Walk up the parents until we hit the root, looking for a comment node
|
|
526
|
-
for (let cur: any = node; cur; cur = cur.parent) {
|
|
527
|
-
if (
|
|
528
|
-
cur.type.is('Comment') || // grammars that group comments
|
|
529
|
-
cur.type.is('comment') || // generic lowercase group
|
|
530
|
-
/Comment$/.test(cur.type.name) // fallback for e.g. LineComment
|
|
531
|
-
) {
|
|
532
|
-
inComment = true;
|
|
533
|
-
break;
|
|
534
|
-
}
|
|
472
|
+
// const { setIsAIChatOpen } = useContext(VZCodeContext);
|
|
473
|
+
|
|
474
|
+
// Widget appears after instances of "todo" in code editor, allowing AI to implement todo tasks when clicked
|
|
475
|
+
function createToDoPlugin() {
|
|
476
|
+
// setIsAIChatOpen: (open: boolean) => void,
|
|
477
|
+
class ToDoWidget extends WidgetType {
|
|
478
|
+
constructor() {
|
|
479
|
+
super();
|
|
535
480
|
}
|
|
536
|
-
if (!inComment) continue; // skip non-comment TODOs
|
|
537
481
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}).range(to);
|
|
542
|
-
widgets.push(deco);
|
|
543
|
-
}
|
|
544
|
-
return Decoration.set(widgets);
|
|
545
|
-
}
|
|
482
|
+
eq(_other: ToDoWidget) {
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
546
485
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
486
|
+
toDOM() {
|
|
487
|
+
const wrap = document.createElement('i');
|
|
488
|
+
wrap.style.display = 'inline-flex'; // prevent block expansion
|
|
489
|
+
wrap.style.alignItems = 'center';
|
|
490
|
+
wrap.style.justifyContent = 'center';
|
|
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
|
+
|
|
502
|
+
const reactContainer =
|
|
503
|
+
document.createElement('div');
|
|
504
|
+
wrap.appendChild(reactContainer);
|
|
505
|
+
|
|
506
|
+
const root = createRoot(reactContainer);
|
|
507
|
+
root.render(
|
|
508
|
+
<SparklesSVG width={14} height={14} />,
|
|
509
|
+
);
|
|
510
|
+
|
|
511
|
+
return wrap;
|
|
512
|
+
}
|
|
550
513
|
|
|
551
|
-
|
|
552
|
-
|
|
514
|
+
ignoreEvent() {
|
|
515
|
+
return false;
|
|
553
516
|
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function toDoWidgets(editor: EditorView) {
|
|
520
|
+
const { state } = editor;
|
|
521
|
+
const tree = syntaxTree(state);
|
|
522
|
+
const findToDo = new RegExpCursor(
|
|
523
|
+
state.doc,
|
|
524
|
+
'todo',
|
|
525
|
+
{
|
|
526
|
+
ignoreCase: true,
|
|
527
|
+
},
|
|
528
|
+
);
|
|
529
|
+
const widgets: Range<Decoration>[] = [];
|
|
530
|
+
|
|
531
|
+
while (!findToDo.next().done) {
|
|
532
|
+
const { from, to } = findToDo.value;
|
|
533
|
+
const node = tree.resolve(from);
|
|
534
|
+
let inComment = false;
|
|
535
|
+
|
|
536
|
+
// Walk up the parents until we hit the root, looking for a comment node
|
|
537
|
+
for (let cur: any = node; cur; cur = cur.parent) {
|
|
538
|
+
if (
|
|
539
|
+
cur.type.is('Comment') || // grammars that group comments
|
|
540
|
+
cur.type.is('comment') || // generic lowercase group
|
|
541
|
+
/Comment$/.test(cur.type.name) // fallback for e.g. LineComment
|
|
542
|
+
) {
|
|
543
|
+
inComment = true;
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
if (!inComment) continue; // skip non-comment TODOs
|
|
554
548
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
syntaxTree(update.state)
|
|
561
|
-
)
|
|
562
|
-
this.decorations = toDoWidgets(update.view);
|
|
549
|
+
const deco = Decoration.widget({
|
|
550
|
+
widget: new ToDoWidget(),
|
|
551
|
+
side: 1,
|
|
552
|
+
}).range(to);
|
|
553
|
+
widgets.push(deco);
|
|
563
554
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
decorations: (v) => v.decorations,
|
|
567
|
-
},
|
|
568
|
-
);
|
|
555
|
+
return Decoration.set(widgets);
|
|
556
|
+
}
|
|
569
557
|
|
|
570
|
-
|
|
571
|
-
|
|
558
|
+
const todoPlugin = ViewPlugin.fromClass(
|
|
559
|
+
class {
|
|
560
|
+
decorations: DecorationSet;
|
|
561
|
+
|
|
562
|
+
constructor(view: EditorView) {
|
|
563
|
+
this.decorations = toDoWidgets(view);
|
|
564
|
+
}
|
|
572
565
|
|
|
573
|
-
|
|
566
|
+
update(update: ViewUpdate) {
|
|
567
|
+
if (
|
|
568
|
+
update.docChanged ||
|
|
569
|
+
update.viewportChanged ||
|
|
570
|
+
syntaxTree(update.startState) !==
|
|
571
|
+
syntaxTree(update.state)
|
|
572
|
+
)
|
|
573
|
+
this.decorations = toDoWidgets(update.view);
|
|
574
|
+
}
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
decorations: (v) => v.decorations,
|
|
578
|
+
},
|
|
579
|
+
);
|
|
580
|
+
|
|
581
|
+
return todoPlugin;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
if (enableToDoPlugin) {
|
|
585
|
+
extensions.push(createToDoPlugin());
|
|
586
|
+
}
|
|
587
|
+
}
|
|
574
588
|
|
|
575
589
|
const editor = new EditorView({
|
|
576
590
|
state: EditorState.create({
|
|
@@ -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,
|
package/src/client/VZRight.tsx
CHANGED
|
@@ -92,6 +92,10 @@ export const VZRight = () => {
|
|
|
92
92
|
enableHotReloading: isInteracting,
|
|
93
93
|
enableSourcemap: true,
|
|
94
94
|
vizId: 'example-viz',
|
|
95
|
+
|
|
96
|
+
// Don't clear the console here in VZCode, since
|
|
97
|
+
// we often want to see debug logs across multiple runs.
|
|
98
|
+
clearConsole: false,
|
|
95
99
|
});
|
|
96
100
|
isFirstRunRef.current = false;
|
|
97
101
|
}
|