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.
Files changed (40) hide show
  1. package/README.md +359 -0
  2. package/dist/assets/{index-D6-he0oi.js → index-CfDs-dAu.js} +99 -99
  3. package/dist/assets/{index-fYq6iaqF.css → index-fSroLVgi.css} +1 -1
  4. package/dist/index.html +2 -2
  5. package/dist/llm-streaming-server/aiEditing.js +58 -0
  6. package/dist/llm-streaming-server/chatOperations.js +494 -0
  7. package/dist/llm-streaming-server/errorHandling.js +49 -0
  8. package/dist/llm-streaming-server/index.js +20 -0
  9. package/dist/llm-streaming-server/llmStreaming.js +265 -0
  10. package/dist/llm-streaming-server/validation.js +19 -0
  11. package/dist/server/aiChatHandler/index.js +5 -5
  12. package/package.json +35 -35
  13. package/src/client/CodeEditor/getOrCreateEditor.tsx +20 -13
  14. package/src/client/CodeEditor/index.tsx +3 -3
  15. package/src/client/VZSidebar/AIChat/styles.scss +38 -0
  16. package/src/client/VZSidebar/FileTypeIcon.tsx +1 -0
  17. package/src/client/VZSidebar/index.tsx +1 -1
  18. package/src/llm-streaming-server/README.md +71 -0
  19. package/src/llm-streaming-server/aiEditing.ts +102 -0
  20. package/src/llm-streaming-server/chatOperations.ts +655 -0
  21. package/src/llm-streaming-server/errorHandling.ts +66 -0
  22. package/src/llm-streaming-server/index.ts +51 -0
  23. package/src/llm-streaming-server/llmStreaming.ts +403 -0
  24. package/src/llm-streaming-server/validation.ts +24 -0
  25. package/src/llm-streaming-ui/README.md +103 -0
  26. package/src/llm-streaming-ui/components/ChatInput.tsx +237 -0
  27. package/src/llm-streaming-ui/components/DiffView.scss +173 -0
  28. package/src/llm-streaming-ui/components/DiffView.tsx +236 -0
  29. package/src/llm-streaming-ui/components/FileEditingIndicator.tsx +89 -0
  30. package/src/llm-streaming-ui/components/IndividualFileDiff.tsx +86 -0
  31. package/src/llm-streaming-ui/components/JumpToLatestButton.tsx +64 -0
  32. package/src/llm-streaming-ui/components/Message.tsx +145 -0
  33. package/src/llm-streaming-ui/components/MessageList.tsx +241 -0
  34. package/src/llm-streaming-ui/components/ThinkingScratchpad.tsx +42 -0
  35. package/src/llm-streaming-ui/components/TypingIndicator.tsx +19 -0
  36. package/src/llm-streaming-ui/components/index.tsx +388 -0
  37. package/src/llm-streaming-ui/components/styles.scss +831 -0
  38. package/src/llm-streaming-ui/components/useSpeechRecognition.ts +116 -0
  39. package/src/llm-streaming-ui/index.ts +34 -0
  40. package/src/server/aiChatHandler/index.ts +5 -5
package/README.md CHANGED
@@ -10,6 +10,7 @@ VZCode offers a multiplayer code editing environment that caters to a real-time
10
10
  - [Development](#development)
11
11
  - [Features](#features)
12
12
  - [Use Cases](#use-cases)
13
+ - [Visual Editor](#visual-editor)
13
14
  - [Stack](#stack)
14
15
  - [Goals](#goals)
15
16
  - [Prior Work](#prior-work)
@@ -120,6 +121,364 @@ You can also use [npm link](https://docs.npmjs.com/cli/v8/commands/npm-link) to
120
121
  - **Staging Site Editor (Experimental)**:
121
122
  Use VZCode on a persistent server, making code changes with multiplayer mode remotely, reflecting instantly on a staging site.
122
123
 
124
+ ## Visual Editor
125
+
126
+ VZCode includes a visual editor feature that allows you to create interactive widgets (like sliders and color pickers) for tweaking configuration parameters in real-time. This is particularly useful for data visualizations where you want to adjust visual properties dynamically without editing code.
127
+
128
+ ### Overview
129
+
130
+ The visual editor works by:
131
+
132
+ 1. Loading a `config.json` file that defines configuration parameters
133
+ 2. Defining interactive widgets in the `visualEditorWidgets` array
134
+ 3. Listening for configuration updates via `postMessage`
135
+ 4. Re-rendering the visualization when configuration changes
136
+
137
+ ### Configuration Structure
138
+
139
+ Your project should include a `config.json` file with the following structure:
140
+
141
+ ```json
142
+ {
143
+ "xValue": "sepal_length",
144
+ "yValue": "sepal_width",
145
+ "margin": {
146
+ "top": 20,
147
+ "right": 67,
148
+ "bottom": 60,
149
+ "left": 60
150
+ },
151
+ "fontSize": "14px",
152
+ "fontFamily": "sans-serif",
153
+ "pointRadius": 17.2675034867503,
154
+ "pointFill": "black",
155
+ "pointOpacity": 0.7,
156
+ "loadingFontSize": "24px",
157
+ "loadingFontFamily": "sans-serif",
158
+ "loadingMessage": "Loading...",
159
+ "dataUrl": "iris.csv",
160
+ "colorScale": {
161
+ "setosa": "#1f77b4",
162
+ "versicolor": "#ff7f0e",
163
+ "virginica": "#2ca02c"
164
+ },
165
+ "visualEditorWidgets": [
166
+ {
167
+ "type": "slider",
168
+ "label": "Point Radius",
169
+ "property": "pointRadius",
170
+ "min": 1,
171
+ "max": 30
172
+ },
173
+ {
174
+ "type": "slider",
175
+ "label": "Left Margin",
176
+ "property": "margin.left",
177
+ "min": 0,
178
+ "max": 200
179
+ }
180
+ ]
181
+ }
182
+ ```
183
+
184
+ ### Widget Configuration
185
+
186
+ The `visualEditorWidgets` array defines the interactive controls that will be available in the visual editor. Each widget has the following properties:
187
+
188
+ - **type**: The type of widget. Supported types are:
189
+ - `"slider"` - A slider control for numeric values
190
+ - `"checkbox"` - A checkbox for boolean values
191
+ - `"textInput"` - A text input field for string values
192
+ - `"dropdown"` - A dropdown selector with predefined options
193
+ - `"color"` - A color picker with HCL (Hue, Chroma, Lightness) sliders
194
+ - **label**: Human-readable label displayed in the UI
195
+ - **property**: The configuration property to modify (supports nested properties using dot notation like `"margin.left"`)
196
+ - **min**: Minimum value (required for `slider` widgets)
197
+ - **max**: Maximum value (required for `slider` widgets)
198
+ - **step**: Step increment for `slider` widgets (optional, defaults to 1 if not specified)
199
+ - **options**: Array of string options (required for `dropdown` widgets)
200
+
201
+ ### Setting Up Your Visualization
202
+
203
+ To make your visualization work with the visual editor, you need to:
204
+
205
+ #### 1. Load the Configuration
206
+
207
+ Use d3-rosetta's state management to load the configuration file:
208
+
209
+ ```javascript
210
+ import { createStateField } from 'd3-rosetta';
211
+ import { json } from 'd3';
212
+
213
+ export const viz = (container, state, setState) => {
214
+ const stateField = createStateField(state, setState);
215
+ const [config, setConfig] = stateField('config');
216
+
217
+ // Load config first if not already loaded
218
+ if (!config) {
219
+ json('config.json')
220
+ .then((loadedConfig) => {
221
+ setConfig(loadedConfig);
222
+ })
223
+ .catch((error) => {
224
+ console.error('Failed to load config:', error);
225
+ });
226
+ return;
227
+ }
228
+
229
+ // ... rest of your visualization code
230
+ };
231
+ ```
232
+
233
+ #### 2. Set Up postMessage Event Listener
234
+
235
+ Add an event listener to receive configuration updates from the visual editor:
236
+
237
+ ```javascript
238
+ export const viz = (container, state, setState) => {
239
+ // ... other code ...
240
+
241
+ // Set up postMessage event listener if not already set
242
+ if (!state.eventListenerAttached) {
243
+ window.addEventListener('message', (event) => {
244
+ // Verify the message contains config data
245
+ if (event.data && typeof event.data === 'object') {
246
+ // Update the config with the received data
247
+ setState((state) => ({
248
+ ...state,
249
+ config: {
250
+ ...state.config,
251
+ ...event.data,
252
+ },
253
+ }));
254
+ }
255
+ });
256
+
257
+ // Mark that we've attached the event listener to avoid duplicates
258
+ setState((prevState) => ({
259
+ ...prevState,
260
+ eventListenerAttached: true,
261
+ }));
262
+ }
263
+
264
+ // ... rest of your visualization code
265
+ };
266
+ ```
267
+
268
+ #### 3. Use Configuration in Your Rendering
269
+
270
+ Use the configuration values when rendering your visualization:
271
+
272
+ ```javascript
273
+ // Example: Rendering data points with configurable properties
274
+ export const renderMarks = (
275
+ svg,
276
+ {
277
+ data,
278
+ xScale,
279
+ yScale,
280
+ xValue,
281
+ yValue,
282
+ pointRadius,
283
+ colorScale,
284
+ pointOpacity,
285
+ },
286
+ ) =>
287
+ svg
288
+ .selectAll('circle.data-point')
289
+ .data(data)
290
+ .join('circle')
291
+ .attr('class', 'data-point')
292
+ .attr('cx', (d) => xScale(xValue(d)))
293
+ .attr('cy', (d) => yScale(yValue(d)))
294
+ .attr('r', pointRadius) // Uses config value
295
+ .attr('fill', (d) => colorScale[d.species])
296
+ .attr('opacity', pointOpacity); // Uses config value
297
+ ```
298
+
299
+ #### 4. Handle Loading States
300
+
301
+ Display loading states while data is being fetched:
302
+
303
+ ```javascript
304
+ export const renderLoadingState = (
305
+ svg,
306
+ { x, y, text, shouldShow, fontSize, fontFamily },
307
+ ) => {
308
+ svg
309
+ .selectAll('text.loading-text')
310
+ .data(shouldShow ? [null] : [])
311
+ .join('text')
312
+ .attr('class', 'loading-text')
313
+ .attr('x', x)
314
+ .attr('y', y)
315
+ .attr('text-anchor', 'middle')
316
+ .attr('dominant-baseline', 'middle')
317
+ .attr('font-size', fontSize) // Uses config value
318
+ .attr('font-family', fontFamily) // Uses config value
319
+ .text(text);
320
+ };
321
+ ```
322
+
323
+ #### 5. Manage Asynchronous Data Loading
324
+
325
+ Handle asynchronous data requests with proper state management:
326
+
327
+ ```javascript
328
+ export const asyncRequest = (
329
+ setDataRequest,
330
+ loadAndParseData,
331
+ ) => {
332
+ setDataRequest({ status: 'Loading' });
333
+ loadAndParseData()
334
+ .then((data) => {
335
+ setDataRequest({ status: 'Succeeded', data });
336
+ })
337
+ .catch((error) => {
338
+ setDataRequest({ status: 'Failed', error });
339
+ });
340
+ };
341
+ ```
342
+
343
+ ### Complete Example
344
+
345
+ Here's how everything fits together in your main visualization file:
346
+
347
+ ```javascript
348
+ import { createStateField } from 'd3-rosetta';
349
+ import { setupSVG } from './setupSVG.js';
350
+ import { renderLoadingState } from './renderLoadingState.js';
351
+ import { asyncRequest } from './asyncRequest.js';
352
+ import { loadAndParseData } from './loadAndParseData.js';
353
+ import { scatterPlot } from './scatterPlot.js';
354
+ import { measureDimensions } from './measureDimensions.js';
355
+ import { json } from 'd3';
356
+
357
+ export const viz = (container, state, setState) => {
358
+ const stateField = createStateField(state, setState);
359
+ const [dataRequest, setDataRequest] =
360
+ stateField('dataRequest');
361
+ const [config, setConfig] = stateField('config');
362
+
363
+ // Set up postMessage event listener if not already set
364
+ if (!state.eventListenerAttached) {
365
+ window.addEventListener('message', (event) => {
366
+ if (event.data && typeof event.data === 'object') {
367
+ setState((state) => ({
368
+ ...state,
369
+ config: {
370
+ ...state.config,
371
+ ...event.data,
372
+ },
373
+ }));
374
+ }
375
+ });
376
+
377
+ setState((prevState) => ({
378
+ ...prevState,
379
+ eventListenerAttached: true,
380
+ }));
381
+ }
382
+
383
+ // Load config first if not already loaded
384
+ if (!config) {
385
+ json('config.json')
386
+ .then((loadedConfig) => {
387
+ setConfig(loadedConfig);
388
+ })
389
+ .catch((error) => {
390
+ console.error('Failed to load config:', error);
391
+ });
392
+ return;
393
+ }
394
+
395
+ // After config is loaded, load the data
396
+ if (!dataRequest) {
397
+ return asyncRequest(setDataRequest, () =>
398
+ loadAndParseData(config.dataUrl),
399
+ );
400
+ }
401
+
402
+ const { data, error } = dataRequest;
403
+ const dimensions = measureDimensions(container);
404
+ const svg = setupSVG(container, dimensions);
405
+
406
+ renderLoadingState(svg, {
407
+ shouldShow: !data,
408
+ text: error
409
+ ? `Error: ${error.message}`
410
+ : config.loadingMessage,
411
+ x: dimensions.width / 2,
412
+ y: dimensions.height / 2,
413
+ fontSize: config.loadingFontSize,
414
+ fontFamily: config.loadingFontFamily,
415
+ });
416
+
417
+ if (data) {
418
+ // Transform string properties in config to accessor functions
419
+ const configWithAccessors = {
420
+ ...config,
421
+ xValue: (d) => d[config.xValue],
422
+ yValue: (d) => d[config.yValue],
423
+ };
424
+
425
+ scatterPlot(svg, {
426
+ ...configWithAccessors,
427
+ data,
428
+ dimensions,
429
+ });
430
+ }
431
+ };
432
+ ```
433
+
434
+ ### Required HTML Structure
435
+
436
+ Your `index.html` should include a container element and proper script imports:
437
+
438
+ ```html
439
+ <!DOCTYPE html>
440
+ <html>
441
+ <head>
442
+ <meta charset="utf-8" />
443
+ <title>Visual Editor Example</title>
444
+ <link rel="stylesheet" href="styles.css" />
445
+ <script type="importmap">
446
+ {
447
+ "imports": {
448
+ "d3": "https://cdn.jsdelivr.net/npm/d3@7.9.0/+esm",
449
+ "d3-rosetta": "https://cdn.jsdelivr.net/npm/d3-rosetta@3.0.0/+esm"
450
+ }
451
+ }
452
+ </script>
453
+ </head>
454
+ <body>
455
+ <div id="viz-container"></div>
456
+ <script type="module" src="index.js"></script>
457
+ </body>
458
+ </html>
459
+ ```
460
+
461
+ ### Styling
462
+
463
+ Use CSS to ensure your visualization container fills the viewport:
464
+
465
+ ```css
466
+ #viz-container {
467
+ position: fixed;
468
+ inset: 0;
469
+ }
470
+ ```
471
+
472
+ ### Best Practices
473
+
474
+ 1. **Use d3-rosetta**: The visual editor is designed to work with d3-rosetta's unidirectional data flow pattern
475
+ 2. **Modular Code**: Separate your rendering logic into small, focused functions
476
+ 3. **Configuration-Driven**: Make visual properties configurable through `config.json`
477
+ 4. **Nested Properties**: Use dot notation (e.g., `"margin.left"`) to modify nested configuration values
478
+ 5. **Hot Reloading**: The visual editor works seamlessly with VZCode's throttled auto-save for hot reloading environments
479
+
480
+ For a complete working example, see the [visualEditor sample directory](test/sampleDirectories/visualEditor) in this repository.
481
+
123
482
  ## Stack
124
483
 
125
484
  Built using technologies such as: