vzcode 2.10.0 → 2.11.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/index.html CHANGED
@@ -20,8 +20,8 @@
20
20
  href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap"
21
21
  rel="stylesheet"
22
22
  />
23
- <script type="module" crossorigin src="/assets/index-CsDf6ybm.js"></script>
24
- <link rel="stylesheet" crossorigin href="/assets/index-Bjj9VRMn.css">
23
+ <script type="module" crossorigin src="/assets/index-BK1qsxog.js"></script>
24
+ <link rel="stylesheet" crossorigin href="/assets/index-C5d5win4.css">
25
25
  </head>
26
26
  <body>
27
27
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vzcode",
3
- "version": "2.10.0",
3
+ "version": "2.11.0",
4
4
  "description": "Multiplayer code editor system",
5
5
  "main": "src/index.ts",
6
6
  "type": "module",
@@ -110,7 +110,7 @@
110
110
  },
111
111
  "homepage": "https://github.com/vizhub-core/vzcode#readme",
112
112
  "dependencies": {
113
- "@codemirror/autocomplete": "^6.18.6",
113
+ "@codemirror/autocomplete": "^6.18.7",
114
114
  "@codemirror/lang-css": "^6.3.1",
115
115
  "@codemirror/lang-html": "^6.4.9",
116
116
  "@codemirror/lang-javascript": "^6.2.4",
@@ -119,9 +119,9 @@
119
119
  "@codemirror/lint": "^6.8.5",
120
120
  "@codemirror/state": "^6.5.2",
121
121
  "@codemirror/theme-one-dark": "^6.1.3",
122
- "@codemirror/view": "^6.38.1",
123
- "@langchain/core": "^0.3.71",
124
- "@langchain/openai": "^0.6.7",
122
+ "@codemirror/view": "^6.38.2",
123
+ "@langchain/core": "^0.3.73",
124
+ "@langchain/openai": "^0.6.11",
125
125
  "@lezer/highlight": "^1.2.1",
126
126
  "@livekit/components-react": "^2.9.14",
127
127
  "@replit/codemirror-indentation-markers": "^6.5.3",
@@ -141,8 +141,8 @@
141
141
  "@uiw/codemirror-themes": "^4.25.1",
142
142
  "@valtown/codemirror-ts": "^2.3.1",
143
143
  "@vizhub/runtime": "^4.4.0",
144
- "@vizhub/viz-types": "^0.4.0",
145
- "@vizhub/viz-utils": "^1.4.0",
144
+ "@vizhub/viz-types": "^0.5.0",
145
+ "@vizhub/viz-utils": "^1.5.0",
146
146
  "body-parser": "^2.2.0",
147
147
  "bootstrap-icons": "^1.13.1",
148
148
  "codemirror": "^6.0.2",
@@ -151,19 +151,20 @@
151
151
  "color-hash": "^2.0.2",
152
152
  "comlink": "^4.4.2",
153
153
  "d3-array": "^3.2.4",
154
+ "d3-color": "^3.1.0",
154
155
  "diff": "^8.0.2",
155
156
  "diff-match-patch": "^1.0.5",
156
157
  "diff2html": "^3.4.52",
157
- "dotenv": "^17.2.1",
158
+ "dotenv": "^17.2.2",
158
159
  "editcodewithai": "^2.3.0",
159
- "eslint-linter-browserify": "^9.33.0",
160
+ "eslint-linter-browserify": "^9.34.0",
160
161
  "express": "^5.1.0",
161
162
  "ignore": "^7.0.5",
162
163
  "json0-ot-diff": "^1.1.2",
163
164
  "jszip": "^3.10.1",
164
- "livekit-server-sdk": "^2.13.2",
165
+ "livekit-server-sdk": "^2.13.3",
165
166
  "llm-code-format": "^3.0.0",
166
- "lucide-react": "^0.539.0",
167
+ "lucide-react": "^0.542.0",
167
168
  "npm": "^11.5.2",
168
169
  "open": "^10.2.0",
169
170
  "prettier-plugin-svelte": "^3.4.0",
@@ -171,7 +172,7 @@
171
172
  "react-bootstrap": "^2.10.10",
172
173
  "react-dom": "^18",
173
174
  "react-markdown": "^10.1.0",
174
- "react-router-dom": "^7.8.1",
175
+ "react-router-dom": "^7.8.2",
175
176
  "remark-gfm": "^4.0.1",
176
177
  "sharedb": "^5.2.2",
177
178
  "sharedb-client-browser": "^5.2.2",
@@ -180,30 +181,31 @@
180
181
  "ws": "^8.18.3"
181
182
  },
182
183
  "devDependencies": {
183
- "@eslint/js": "^9.33.0",
184
- "@tauri-apps/cli": "^2.7.1",
184
+ "@eslint/js": "^9.34.0",
185
+ "@tauri-apps/cli": "^2.8.4",
186
+ "@types/d3-color": "^3.1.3",
185
187
  "@types/react": "^18",
186
188
  "@types/react-dom": "^18",
187
- "@typescript-eslint/eslint-plugin": "^8.39.1",
188
- "@typescript-eslint/parser": "^8.39.1",
189
- "@vitejs/plugin-react": "^5.0.0",
190
- "concurrently": "^9.2.0",
189
+ "@typescript-eslint/eslint-plugin": "^8.42.0",
190
+ "@typescript-eslint/parser": "^8.42.0",
191
+ "@vitejs/plugin-react": "^5.0.2",
192
+ "concurrently": "^9.2.1",
191
193
  "cross-env": "^10.0.0",
192
- "eslint": "^9.33.0",
194
+ "eslint": "^9.34.0",
193
195
  "eslint-plugin-jsx-a11y": "^6.10.2",
194
196
  "eslint-plugin-react": "^7.37.5",
195
197
  "eslint-plugin-react-hooks": "^5.2.0",
196
198
  "globals": "^16.3.0",
197
- "npm-check-updates": "^18.0.2",
199
+ "npm-check-updates": "^18.0.3",
198
200
  "prettier": "^3.6.2",
199
- "sass": "^1.90.0",
201
+ "sass": "^1.92.0",
200
202
  "ts-node": "^10.9.2",
201
203
  "typescript": "^5.9.2",
202
- "vite": "^7.1.2",
204
+ "vite": "^7.1.4",
203
205
  "vitest": "^3.2.4"
204
206
  },
205
207
  "optionalDependencies": {
206
- "@rollup/rollup-darwin-arm64": "^4.46.2",
207
- "@rollup/rollup-win32-x64-msvc": "^4.46.2"
208
+ "@rollup/rollup-darwin-arm64": "^4.50.0",
209
+ "@rollup/rollup-win32-x64-msvc": "^4.50.0"
208
210
  }
209
211
  }
@@ -15,6 +15,7 @@ interface MessageProps {
15
15
  isStreaming?: boolean;
16
16
  diffData?: UnifiedFilesDiff;
17
17
  chatId?: string;
18
+ showAdditionalWidgets?: boolean;
18
19
  }
19
20
 
20
21
  const MessageComponent = ({
@@ -25,6 +26,7 @@ const MessageComponent = ({
25
26
  isStreaming,
26
27
  diffData,
27
28
  chatId,
29
+ showAdditionalWidgets = false,
28
30
  }: MessageProps) => {
29
31
  const { additionalWidgets, handleSendMessage } =
30
32
  useContext(VZCodeContext);
@@ -56,7 +58,8 @@ const MessageComponent = ({
56
58
  Object.keys(diffData).length > 0 && (
57
59
  <DiffView diffData={diffData} />
58
60
  )}
59
- {additionalWidgets &&
61
+ {showAdditionalWidgets &&
62
+ additionalWidgets &&
60
63
  chatId &&
61
64
  additionalWidgets({
62
65
  messageId: id,
@@ -152,13 +152,24 @@ const MessageListComponent = ({
152
152
  aiScratchpad && aiScratchpad.trim(),
153
153
  );
154
154
 
155
+ // Find the most recent assistant message index
156
+ const lastAssistantMessageIndex = messages
157
+ .map((m, i) => (m.role === 'assistant' ? i : -1))
158
+ .filter((i) => i !== -1)
159
+ .pop();
160
+
155
161
  return (
156
162
  <div
157
163
  className="ai-chat-messages"
158
164
  ref={messagesContainerRef}
159
165
  onScroll={handleScroll}
160
166
  >
161
- {messages.map((msg, _index) => {
167
+ {messages.map((msg, index) => {
168
+ // Only show additionalWidgets for the most recent assistant message
169
+ const showAdditionalWidgets =
170
+ msg.role === 'assistant' &&
171
+ index === lastAssistantMessageIndex;
172
+
162
173
  return (
163
174
  <Message
164
175
  key={msg.id}
@@ -168,6 +179,7 @@ const MessageListComponent = ({
168
179
  timestamp={msg.timestamp}
169
180
  diffData={(msg as any).diffData}
170
181
  chatId={chatId}
182
+ showAdditionalWidgets={showAdditionalWidgets}
171
183
  />
172
184
  );
173
185
  })}
@@ -9,6 +9,7 @@ import { VZCodeContext } from '../VZCodeContext';
9
9
  import { VizContent, VizFileId } from '@vizhub/viz-types';
10
10
  import { VisualEditorConfigEntry } from '../../types';
11
11
  import { EmptyState } from './EmptyState';
12
+ import { color, hcl, HCLColor, rgb } from 'd3-color';
12
13
 
13
14
  const CONFIG_FILE_NAME = 'config.json';
14
15
 
@@ -229,6 +230,90 @@ export const VisualEditor = () => {
229
230
  };
230
231
  }, [openDropdown]);
231
232
 
233
+ const onColorChange = useCallback(
234
+ (property: string, lchComponent: 'l' | 'c' | 'h') =>
235
+ (event: React.ChangeEvent<HTMLInputElement>) => {
236
+ const newValue = parseFloat(
237
+ event.currentTarget.value,
238
+ );
239
+
240
+ // Get current hex color from config
241
+ const currentHex =
242
+ configData[property] || '#000000';
243
+
244
+ // Convert current hex to LCH
245
+ let hclFromRGB: HCLColor;
246
+ try {
247
+ const rgbColor = rgb(currentHex);
248
+ hclFromRGB = hcl(rgbColor);
249
+ } catch (error) {
250
+ // Fallback to black if conversion fails
251
+ hclFromRGB = hcl('black');
252
+ }
253
+
254
+ let hclArray: number[] = [
255
+ (localValues[`${property}_h`] as number) ??
256
+ hclFromRGB.h,
257
+ (localValues[`${property}_c`] as number) ??
258
+ hclFromRGB.c,
259
+ (localValues[`${property}_l`] as number) ??
260
+ hclFromRGB.l,
261
+ ];
262
+
263
+ // Update the specific LCH component
264
+ const newhcl = [...hclArray];
265
+ if (lchComponent === 'h') newhcl[0] = newValue;
266
+ else if (lchComponent === 'c') newhcl[1] = newValue;
267
+ else if (lchComponent === 'l') newhcl[2] = newValue;
268
+
269
+ // Convert back to hex
270
+ let newHex;
271
+ try {
272
+ const newColor = hcl(
273
+ newhcl[0],
274
+ newhcl[1],
275
+ newhcl[2],
276
+ );
277
+
278
+ if (newColor.displayable()) {
279
+ newHex = newColor.formatHex();
280
+ } else {
281
+ newHex = currentHex;
282
+ }
283
+ } catch (error) {
284
+ // Fallback to current color if conversion fails
285
+ newHex = currentHex;
286
+ }
287
+
288
+ // Update local state for responsive UI
289
+ setLocalValues((prev) => ({
290
+ ...prev,
291
+ [property]: newHex,
292
+ [`${property}_h`]: newhcl[0],
293
+ [`${property}_c`]: newhcl[1],
294
+ [`${property}_l`]: newhcl[2],
295
+ }));
296
+
297
+ // Update config.json with hex value
298
+ const newConfigData = {
299
+ ...configData,
300
+ [property]: newHex,
301
+ };
302
+
303
+ submitOperation((document: VizContent) => ({
304
+ ...document,
305
+ files: {
306
+ ...files,
307
+ [configFileId]: {
308
+ name: 'config.json',
309
+ text: JSON.stringify(newConfigData, null, 2),
310
+ },
311
+ },
312
+ }));
313
+ },
314
+ [configData, files, configFileId, setLocalValues],
315
+ );
316
+
232
317
  const visualEditorWidgets: VisualEditorConfigEntry[] =
233
318
  configData?.visualEditorWidgets ?? [];
234
319
 
@@ -250,6 +335,32 @@ export const VisualEditor = () => {
250
335
  } else if (widget.type === 'dropdown') {
251
336
  newLocalValues[widget.property] =
252
337
  configData[widget.property];
338
+ } else if (widget.type === 'color') {
339
+ newLocalValues[widget.property] =
340
+ configData[widget.property];
341
+
342
+ // Convert hex to LCH for internal state
343
+ const hexColor = configData[widget.property];
344
+
345
+ if (localValues[widget.property] !== hexColor) {
346
+ if (hexColor) {
347
+ try {
348
+ const rgbColor = color(hexColor);
349
+ const lch = hcl(rgbColor);
350
+ newLocalValues[`${widget.property}_h`] =
351
+ lch.h;
352
+ newLocalValues[`${widget.property}_c`] =
353
+ lch.c;
354
+ newLocalValues[`${widget.property}_l`] =
355
+ lch.l;
356
+ } catch (error) {
357
+ // Fallback values if conversion fails
358
+ newLocalValues[`${widget.property}_l`] = 0;
359
+ newLocalValues[`${widget.property}_c`] = 0;
360
+ newLocalValues[`${widget.property}_h`] = 0;
361
+ }
362
+ }
363
+ }
253
364
  }
254
365
  });
255
366
  setLocalValues(newLocalValues);
@@ -524,6 +635,159 @@ export const VisualEditor = () => {
524
635
  </div>
525
636
  </div>
526
637
  );
638
+ } else if (widgetConfig.type === 'color') {
639
+ // Use local value if available, otherwise fall back to config value
640
+ const currentHex =
641
+ localValues[widgetConfig.property] ??
642
+ configData[widgetConfig.property] ??
643
+ '#000000';
644
+
645
+ // Convert hex to LCH for slider values
646
+ let hclColor;
647
+ try {
648
+ const rgbColor = color(currentHex);
649
+ hclColor = hcl(rgbColor);
650
+ } catch (error) {
651
+ hclColor = hcl('black');
652
+ }
653
+
654
+ // Use local LCH values if available, otherwise convert from hex
655
+ const currentH =
656
+ localValues[`${widgetConfig.property}_h`] ??
657
+ hclColor.h;
658
+ const currentC =
659
+ localValues[`${widgetConfig.property}_c`] ??
660
+ hclColor.c;
661
+ const currentL =
662
+ localValues[`${widgetConfig.property}_l`] ??
663
+ hclColor.l;
664
+
665
+ return (
666
+ <div
667
+ key={widgetConfig.property}
668
+ className="visual-editor-color"
669
+ >
670
+ <div className="color-header">
671
+ <label className="color-label">
672
+ {widgetConfig.label}
673
+ </label>
674
+ <span className="color-value">
675
+ {currentHex}
676
+ </span>
677
+ </div>
678
+
679
+ <div
680
+ className="color-preview"
681
+ style={{ backgroundColor: currentHex }}
682
+ />
683
+
684
+ <div className="lch-sliders">
685
+ {/* Lightness Slider */}
686
+ <div className="lch-slider">
687
+ <div className="slider-header">
688
+ <label className="slider-label">
689
+ Lightness
690
+ </label>
691
+ <span className="slider-value">
692
+ {Math.round(currentL)}
693
+ </span>
694
+ </div>
695
+ <div className="slider-container">
696
+ <input
697
+ type="range"
698
+ className="slider-input"
699
+ min={0}
700
+ max={100}
701
+ step={1}
702
+ value={currentL}
703
+ onChange={onColorChange(
704
+ widgetConfig.property,
705
+ 'l',
706
+ )}
707
+ />
708
+ <div
709
+ className="slider-track-fill"
710
+ style={{ width: `${currentL}%` }}
711
+ />
712
+ </div>
713
+ <div className="slider-bounds">
714
+ <span className="min-value">0</span>
715
+ <span className="max-value">100</span>
716
+ </div>
717
+ </div>
718
+
719
+ {/* Chroma Slider */}
720
+ <div className="lch-slider">
721
+ <div className="slider-header">
722
+ <label className="slider-label">
723
+ Chroma
724
+ </label>
725
+ <span className="slider-value">
726
+ {Math.round(currentC)}
727
+ </span>
728
+ </div>
729
+ <div className="slider-container">
730
+ <input
731
+ type="range"
732
+ className="slider-input"
733
+ min={0}
734
+ max={100}
735
+ step={1}
736
+ value={currentC}
737
+ onChange={onColorChange(
738
+ widgetConfig.property,
739
+ 'c',
740
+ )}
741
+ />
742
+ <div
743
+ className="slider-track-fill"
744
+ style={{ width: `${currentC}%` }}
745
+ />
746
+ </div>
747
+ <div className="slider-bounds">
748
+ <span className="min-value">0</span>
749
+ <span className="max-value">100</span>
750
+ </div>
751
+ </div>
752
+
753
+ {/* Hue Slider */}
754
+ <div className="lch-slider">
755
+ <div className="slider-header">
756
+ <label className="slider-label">
757
+ Hue
758
+ </label>
759
+ <span className="slider-value">
760
+ {Math.round(currentH)}°
761
+ </span>
762
+ </div>
763
+ <div className="slider-container">
764
+ <input
765
+ type="range"
766
+ className="slider-input"
767
+ min={0}
768
+ max={360}
769
+ step={1}
770
+ value={currentH}
771
+ onChange={onColorChange(
772
+ widgetConfig.property,
773
+ 'h',
774
+ )}
775
+ />
776
+ <div
777
+ className="slider-track-fill"
778
+ style={{
779
+ width: `${(currentH / 360) * 100}%`,
780
+ }}
781
+ />
782
+ </div>
783
+ <div className="slider-bounds">
784
+ <span className="min-value">0°</span>
785
+ <span className="max-value">360°</span>
786
+ </div>
787
+ </div>
788
+ </div>
789
+ </div>
790
+ );
527
791
  }
528
792
  })}
529
793
  </div>
@@ -396,29 +396,6 @@ export const VZSidebar = ({
396
396
  </i>
397
397
  </OverlayTrigger>
398
398
 
399
- {enableAIChat && (
400
- <OverlayTrigger
401
- placement="right"
402
- overlay={
403
- <Tooltip id="ai-chat-tooltip">
404
- {aiChatToolTipText}
405
- </Tooltip>
406
- }
407
- >
408
- <i
409
- id="ai-chat-icon"
410
- className="icon-button icon-button-dark"
411
- onClick={() => {
412
- setIsAIChatOpen(true);
413
- setIsSearchOpen(false);
414
- setIsVisualEditorOpen(false);
415
- }}
416
- >
417
- <SparklesSVG />
418
- </i>
419
- </OverlayTrigger>
420
- )}
421
-
422
399
  <OverlayTrigger
423
400
  placement="right"
424
401
  overlay={