vzcode 1.15.0 → 1.16.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-DbFdL1A1.js"></script>
24
- <link rel="stylesheet" crossorigin href="/assets/index-Yz-ZHc0O.css">
23
+ <script type="module" crossorigin src="/assets/index-BlwLvENZ.js"></script>
24
+ <link rel="stylesheet" crossorigin href="/assets/index-B6yTvvah.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": "1.15.0",
3
+ "version": "1.16.0",
4
4
  "description": "Multiplayer code editor system",
5
5
  "main": "src/index.ts",
6
6
  "type": "module",
@@ -103,7 +103,7 @@
103
103
  },
104
104
  "homepage": "https://github.com/vizhub-core/vzcode#readme",
105
105
  "dependencies": {
106
- "@codemirror/autocomplete": "^6.17.0",
106
+ "@codemirror/autocomplete": "^6.18.0",
107
107
  "@codemirror/lang-css": "^6.2.1",
108
108
  "@codemirror/lang-html": "^6.4.9",
109
109
  "@codemirror/lang-javascript": "^6.2.2",
@@ -111,7 +111,7 @@
111
111
  "@codemirror/lang-markdown": "^6.2.5",
112
112
  "@codemirror/state": "^6.4.1",
113
113
  "@codemirror/theme-one-dark": "^6.1.2",
114
- "@codemirror/view": "^6.29.1",
114
+ "@codemirror/view": "^6.30.0",
115
115
  "@lezer/highlight": "^1.2.0",
116
116
  "@replit/codemirror-indentation-markers": "^6.5.3",
117
117
  "@replit/codemirror-interact": "^6.3.1",
@@ -140,7 +140,7 @@
140
140
  "json0-ot-diff": "^1.1.2",
141
141
  "ngrok": "^4.3.3",
142
142
  "open": "^10.1.0",
143
- "openai": "^4.53.2",
143
+ "openai": "^4.55.4",
144
144
  "prettier-plugin-svelte": "^3.2.6",
145
145
  "react": "^18.3.1",
146
146
  "react-bootstrap": "^2.10.4",
@@ -160,11 +160,11 @@
160
160
  "prettier": "^3.3.3",
161
161
  "sass": "^1.77.8",
162
162
  "typescript": "^5.5.4",
163
- "vite": "^5.3.5",
163
+ "vite": "^5.4.0",
164
164
  "vitest": "^2.0.5"
165
165
  },
166
166
  "optionalDependencies": {
167
- "@rollup/rollup-darwin-arm64": "^4.19.2",
168
- "@rollup/rollup-win32-x64-msvc": "^4.19.2"
167
+ "@rollup/rollup-darwin-arm64": "^4.20.0",
168
+ "@rollup/rollup-win32-x64-msvc": "^4.20.0"
169
169
  }
170
170
  }
@@ -1,5 +1,6 @@
1
1
  import { useCallback, useContext, useState } from 'react';
2
2
  import { OverlayTrigger, Tooltip } from '../../bootstrap';
3
+ import { FileId, PaneId, TabState } from '../../../types';
3
4
  import { VZCodeContext } from '../../VZCodeContext';
4
5
  import {
5
6
  RequestId,
@@ -7,8 +8,8 @@ import {
7
8
  } from '../../generateRequestId';
8
9
  import { SparklesSVG } from '../../Icons';
9
10
  import { startAIAssist } from '../startAIAssist';
10
- import { Spinner } from '../Spinner';
11
11
  import { editorCacheKey } from '../../useEditorCache';
12
+ import { Spinner } from '../Spinner';
12
13
  import './style.scss';
13
14
 
14
15
  const enableStopGeneration = false;
@@ -18,17 +19,20 @@ export const AIAssistWidget = ({
18
19
  aiAssistOptions,
19
20
  aiAssistTooltipText = 'Start AI Assist',
20
21
  aiAssistClickOverride,
22
+ activeFileId,
23
+ tabList,
24
+ paneId,
21
25
  }: {
22
26
  aiAssistEndpoint: string;
23
27
  aiAssistOptions: { [key: string]: string };
24
28
  aiAssistTooltipText?: string;
25
29
  aiAssistClickOverride?: () => void;
30
+ activeFileId: FileId;
31
+ tabList: Array<TabState>;
32
+ paneId: PaneId;
26
33
  }) => {
27
34
  const {
28
35
  shareDBDoc,
29
- activeFileId,
30
- activePaneId,
31
- tabList,
32
36
  editorCache,
33
37
  runPrettierRef,
34
38
  runCodeRef,
@@ -52,7 +56,7 @@ export const AIAssistWidget = ({
52
56
  // Wait for generation to finish.
53
57
  await startAIAssist({
54
58
  view: editorCache.get(
55
- editorCacheKey(activeFileId, activePaneId),
59
+ editorCacheKey(activeFileId, paneId),
56
60
  ).editor,
57
61
  shareDBDoc,
58
62
  fileId: activeFileId,
@@ -96,7 +100,7 @@ export const AIAssistWidget = ({
96
100
  : currentAIStreamId,
97
101
  );
98
102
  }
99
- }, [activeFileId, activePaneId, aiStreamId]);
103
+ }, [activeFileId, paneId, aiStreamId]);
100
104
 
101
105
  const showWidget = enableStopGeneration
102
106
  ? true
@@ -24,7 +24,7 @@ export const CodeEditor = ({
24
24
  allowGlobals: boolean;
25
25
  }) => {
26
26
  const {
27
- activeFileId,
27
+ activePane,
28
28
  shareDBDoc,
29
29
  content,
30
30
  submitOperation,
@@ -84,7 +84,7 @@ export const CodeEditor = ({
84
84
  const editorCacheValue: EditorCacheValue = useMemo(
85
85
  () =>
86
86
  getOrCreateEditor({
87
- fileId: activeFileId,
87
+ fileId: activePane.activeFileId,
88
88
  shareDBDoc,
89
89
  content,
90
90
  filesPath,
@@ -102,7 +102,7 @@ export const CodeEditor = ({
102
102
  // splitPaneView,
103
103
  }),
104
104
  [
105
- activeFileId,
105
+ activePane,
106
106
  shareDBDoc,
107
107
  filesPath,
108
108
  localPresence,
@@ -5,7 +5,7 @@ import {
5
5
  Decoration,
6
6
  } from '@codemirror/view';
7
7
  import { Annotation, RangeSet } from '@codemirror/state';
8
- import ColorHash from 'color-hash';
8
+ import { assignUserColor } from '../presenceColor';
9
9
  import {
10
10
  FileId,
11
11
  Presence,
@@ -114,11 +114,11 @@ export const json1PresenceDisplay = ({
114
114
  const { start, end } = presence;
115
115
  const from = +start[start.length - 1];
116
116
  const to = +end[end.length - 1];
117
- const userColor = new ColorHash({
118
- lightness: 0.75,
119
- }).rgb(id);
117
+ const userColor = assignUserColor(
118
+ presence.username,
119
+ );
120
120
  const { username } = presence;
121
-
121
+ //console.log("File User Color:" + assignUserColor(presence.username));
122
122
  presenceDecorations.push({
123
123
  from,
124
124
  to: from,
@@ -1,26 +1,23 @@
1
1
  import { useContext } from 'react';
2
+ import { TabState } from '../../types';
2
3
  import { VZCodeContext } from '../VZCodeContext';
3
4
  import { Tab } from './Tab';
4
5
  import './style.scss';
5
- import { TabState } from '../../types';
6
6
 
7
7
  // Displays the list of tabs above the code editor.
8
- export const TabList = () => {
9
- const {
10
- files,
11
- activeFileId,
12
- setActiveFileId,
13
- tabList,
14
- openTab,
15
- closeTabs,
16
- } = useContext(VZCodeContext);
8
+ export const TabList = ({ activeFileId, tabList }) => {
9
+ const { files, setActiveFileId, openTab, closeTabs } =
10
+ useContext(VZCodeContext);
17
11
 
18
12
  return (
19
13
  <div className="vz-tab-list">
20
14
  {files &&
21
15
  tabList
22
16
  // Handles the case that the tab list references a file that was deleted.
23
- .filter((tabState) => tabState.fileId in files)
17
+ .filter(
18
+ (tabState: TabState) =>
19
+ tabState.fileId in files,
20
+ )
24
21
  .map((tabState: TabState) => (
25
22
  <Tab
26
23
  key={tabState.fileId}
@@ -18,6 +18,7 @@ import {
18
18
  PresenceIndicator,
19
19
  Pane,
20
20
  PaneId,
21
+ LeafPane,
21
22
  } from '../types';
22
23
  import { usePrettier } from './usePrettier';
23
24
  import { useTypeScript } from './useTypeScript';
@@ -37,6 +38,7 @@ import {
37
38
  import { useURLSync } from './useURLSync';
38
39
  import { useKeyboardShortcuts } from './useKeyboardShortcuts';
39
40
  import { useRunCode } from './useRunCode';
41
+ import { findPane } from './vzReducer/findPane';
40
42
 
41
43
  // This context centralizes all the "smart" logic
42
44
  // to do with the application state. This includes
@@ -72,14 +74,14 @@ export type VZCodeContextValue = {
72
74
  ) => void;
73
75
  deleteDirectory: (directoryId: string) => void;
74
76
 
75
- activeFileId: string | null;
76
77
  setActiveFileId: (fileId: string | null) => void;
77
78
  setActiveFileLeft: () => void;
78
79
  setActiveFileRight: () => void;
79
80
 
80
81
  activePaneId: PaneId;
82
+ pane: Pane;
83
+ activePane: LeafPane;
81
84
 
82
- tabList: Array<TabState>;
83
85
  openTab: (tabState: TabState) => void;
84
86
  closeTabs: (fileIds: string[]) => void;
85
87
 
@@ -260,12 +262,11 @@ export const VZCodeProvider = ({
260
262
  sidebarPresenceIndicators,
261
263
  } = state;
262
264
 
263
- // TODO support splitPane type
264
- if (pane.type !== 'leafPane') {
265
+ const activePane = findPane(pane, activePaneId);
266
+ if (activePane.type !== 'leafPane') {
267
+ // Should never happen
265
268
  throw new Error('Expected leafPane');
266
269
  }
267
- const tabList = pane.tabList;
268
- const activeFileId = pane.activeFileId;
269
270
 
270
271
  // Functions for dispatching actions to the reducer.
271
272
  const {
@@ -298,8 +299,8 @@ export const VZCodeProvider = ({
298
299
  content,
299
300
  openTab,
300
301
  setActiveFileId,
301
- tabList,
302
- activeFileId,
302
+ tabList: activePane.tabList,
303
+ activeFileId: activePane.activeFileId,
303
304
  });
304
305
 
305
306
  // The set of open directories.
@@ -374,7 +375,8 @@ export const VZCodeProvider = ({
374
375
 
375
376
  useKeyboardShortcuts({
376
377
  closeTabs,
377
- activeFileId,
378
+ // TODO verify that this makes sense
379
+ activeFileId: activePane.activeFileId,
378
380
  activePaneId,
379
381
  handleOpenCreateFileModal,
380
382
  setActiveFileLeft,
@@ -407,14 +409,16 @@ export const VZCodeProvider = ({
407
409
  renameDirectory,
408
410
  deleteDirectory,
409
411
 
410
- activeFileId,
411
412
  setActiveFileId,
412
413
  setActiveFileLeft,
413
414
  setActiveFileRight,
414
415
 
415
416
  activePaneId,
417
+ // Root pane
418
+ pane,
419
+ // Active leaf pane
420
+ activePane,
416
421
 
417
- tabList,
418
422
  openTab,
419
423
  closeTabs,
420
424
 
@@ -9,6 +9,67 @@ import { VZCodeContext } from './VZCodeContext';
9
9
  import { RunCodeWidget } from './RunCodeWidget';
10
10
  import { InteractRule } from '@replit/codemirror-interact';
11
11
 
12
+ // TODO modify this to handle the SplitPane type
13
+ // Recursive structure?
14
+ // SplitPaneView
15
+ // LeafPaneView
16
+ const PaneView = ({
17
+ pane,
18
+ content,
19
+ customInteractRules,
20
+ allowGlobals,
21
+ enableAIAssist,
22
+ aiAssistEndpoint,
23
+ aiAssistOptions,
24
+ aiAssistTooltipText,
25
+ aiAssistClickOverride,
26
+ }) => {
27
+ // This prevents the CodeEditor from rendering
28
+ // during SSR.
29
+ const [isClient, setIsClient] = useState(false);
30
+ useEffect(() => {
31
+ setIsClient(true);
32
+ }, []);
33
+
34
+ // TODO something like
35
+ // return pane.type === 'leafPane' ? (
36
+ // <LeafPaneView />
37
+ // ) : (
38
+ // <SplitPaneView />
39
+ // );
40
+
41
+ // This should go in LeafPaneView
42
+ return (
43
+ <>
44
+ <TabList
45
+ activeFileId={pane.activeFileId}
46
+ tabList={pane.tabList}
47
+ />
48
+ {isClient && content && pane.activeFileId && (
49
+ <CodeEditor
50
+ customInteractRules={customInteractRules}
51
+ allowGlobals={allowGlobals}
52
+ />
53
+ )}
54
+ {isClient &&
55
+ enableAIAssist &&
56
+ content &&
57
+ pane.activeFileId ? (
58
+ <AIAssistWidget
59
+ aiAssistEndpoint={aiAssistEndpoint}
60
+ aiAssistOptions={aiAssistOptions}
61
+ aiAssistTooltipText={aiAssistTooltipText}
62
+ aiAssistClickOverride={aiAssistClickOverride}
63
+ paneId={pane.id}
64
+ activeFileId={pane.activeFileId}
65
+ tabList={pane.tabList}
66
+ />
67
+ ) : null}
68
+ <RunCodeWidget />
69
+ </>
70
+ );
71
+ };
72
+
12
73
  // The middle portion of the VZCode environment, containing:
13
74
  // * The list of tabs at the top
14
75
  // * The code editor itself
@@ -40,41 +101,34 @@ export const VZMiddle = ({
40
101
  content,
41
102
  localPresence,
42
103
  docPresence,
43
- activeFileId,
104
+ pane,
105
+ activePane,
44
106
  errorMessage,
45
107
  } = useContext(VZCodeContext);
46
108
 
47
- // This prevents the CodeEditor from rendering
48
- // during SSR.
49
- const [isClient, setIsClient] = useState(false);
50
- useEffect(() => {
51
- setIsClient(true);
52
- }, []);
109
+ if (activePane.type !== 'leafPane') {
110
+ throw new Error('Expected leafPane');
111
+ }
112
+ // True if the editor panes should be shown.
113
+ const shouldShowEditorPanes: boolean =
114
+ activePane.activeFileId !== null;
53
115
 
54
- return activeFileId !== null ? (
116
+ return shouldShowEditorPanes ? (
55
117
  <div
56
118
  className="middle"
57
119
  style={{ width: codeEditorWidth + 'px' }}
58
120
  >
59
- <TabList />
60
- {isClient && content && activeFileId && (
61
- <CodeEditor
62
- customInteractRules={customInteractRules}
63
- allowGlobals={allowGlobals}
64
- />
65
- )}
66
- {isClient &&
67
- enableAIAssist &&
68
- content &&
69
- activeFileId ? (
70
- <AIAssistWidget
71
- aiAssistEndpoint={aiAssistEndpoint}
72
- aiAssistOptions={aiAssistOptions}
73
- aiAssistTooltipText={aiAssistTooltipText}
74
- aiAssistClickOverride={aiAssistClickOverride}
75
- />
76
- ) : null}
77
- <RunCodeWidget />
121
+ <PaneView
122
+ pane={pane}
123
+ content={content}
124
+ customInteractRules={customInteractRules}
125
+ allowGlobals={allowGlobals}
126
+ enableAIAssist={enableAIAssist}
127
+ aiAssistEndpoint={aiAssistEndpoint}
128
+ aiAssistOptions={aiAssistOptions}
129
+ aiAssistTooltipText={aiAssistTooltipText}
130
+ aiAssistClickOverride={aiAssistClickOverride}
131
+ />
78
132
  <CodeErrorOverlay
79
133
  errorMessage={errorMessage}
80
134
  content={content}
@@ -6,7 +6,6 @@ import {
6
6
  useCallback,
7
7
  useRef,
8
8
  } from 'react';
9
-
10
9
  import {
11
10
  Side,
12
11
  SplitPaneResizeContext,
@@ -44,9 +43,10 @@ export const VZResizer = ({
44
43
 
45
44
  // If there is no active file, don't render the resizer
46
45
  // for the right side.
47
- const { activeFileId } = useContext(VZCodeContext);
46
+ const { activePane } = useContext(VZCodeContext);
47
+
48
48
  const shouldRenderResizer =
49
- side === 'left' || activeFileId;
49
+ side === 'left' || activePane.activeFileId;
50
50
 
51
51
  const previousClientX = useRef<number>(0);
52
52
 
@@ -3,6 +3,7 @@ import { Item } from './Item';
3
3
  import { FileId, PresenceIndicator } from '../../types';
4
4
  import { VZCodeContext } from '../VZCodeContext';
5
5
  import { FileTypeIcon } from './FileTypeIcon';
6
+ import { assignUserColor } from '../presenceColor';
6
7
 
7
8
  const PresenceIndicators = ({
8
9
  presence,
@@ -11,12 +12,25 @@ const PresenceIndicators = ({
11
12
  }) => {
12
13
  return (
13
14
  <div className="presence-indicators">
14
- {presence.map((indicator, index) => (
15
- <div key={index} className="presence-indicator">
16
- {indicator.username[0]}{' '}
17
- {/* Display the first letter of the username */}
18
- </div>
19
- ))}
15
+ {presence.map((indicator, index) => {
16
+ // Get the raw RGB values from assignUserColor
17
+ const color = assignUserColor(indicator.username);
18
+
19
+ // Format it as `rgb()` directly in the component
20
+ const formattedColor = `rgb(${color})`;
21
+
22
+ //console.log(`Username: ${indicator.username}, Assigned Color: ${formattedColor}`);
23
+
24
+ return (
25
+ <div
26
+ key={index}
27
+ className="presence-indicator"
28
+ style={{ backgroundColor: formattedColor }}
29
+ >
30
+ {indicator.username[0]}
31
+ </div>
32
+ );
33
+ })}
20
34
  </div>
21
35
  );
22
36
  };
@@ -19,7 +19,7 @@ export const Listing = ({
19
19
  handleFileClick: (fileId: FileId) => void;
20
20
  handleFileDoubleClick: (fileId: FileId) => void;
21
21
  }) => {
22
- const { activeFileId, sidebarPresenceIndicators } =
22
+ const { activePane, sidebarPresenceIndicators } =
23
23
  useContext(VZCodeContext);
24
24
  const { name, file, fileId } = entity as FileTreeFile;
25
25
  const { path, children } = entity as FileTree;
@@ -35,7 +35,7 @@ export const Listing = ({
35
35
  name={name}
36
36
  handleFileClick={handleFileClick}
37
37
  handleFileDoubleClick={handleFileDoubleClick}
38
- isActive={fileId === activeFileId}
38
+ isActive={fileId === activePane.activeFileId}
39
39
  presence={presence}
40
40
  />
41
41
  ) : (
@@ -290,7 +290,6 @@
290
290
  align-items: center;
291
291
  justify-content: center;
292
292
  font-size: 8px;
293
- background-color: #ff5733;
294
293
  color: #ffffff;
295
294
  }
296
295
  }
@@ -0,0 +1,8 @@
1
+ import ColorHash from 'color-hash';
2
+
3
+ export const assignUserColor = (name: string): string => {
4
+ const userColor = new ColorHash({ lightness: 0.75 })
5
+ .rgb(name)
6
+ .join(',');
7
+ return userColor;
8
+ };