vzcode 0.88.0 → 1.0.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-Dx3HipqX.js"></script>
24
- <link rel="stylesheet" crossorigin href="/assets/index-9yhZQKtK.css">
23
+ <script type="module" crossorigin src="/assets/index-DacyImKg.js"></script>
24
+ <link rel="stylesheet" crossorigin href="/assets/index-CWMOj-be.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": "0.88.0",
3
+ "version": "1.0.0",
4
4
  "description": "Multiplayer code editor system",
5
5
  "main": "src/index.ts",
6
6
  "type": "module",
@@ -59,6 +59,26 @@
59
59
  {
60
60
  "name": "Ethan Nuessle",
61
61
  "url": "https://github.com/enuessle"
62
+ },
63
+ {
64
+ "name": "Brandon Balchand",
65
+ "url": "https://github.com/MandingoBrandon"
66
+ },
67
+ {
68
+ "name": "Demetrius Ho Sang",
69
+ "url": "https://github.com/demhos"
70
+ },
71
+ {
72
+ "name": "Jary Chen",
73
+ "url": "https://github.com/CJary"
74
+ },
75
+ {
76
+ "name": "Shashank Garg",
77
+ "url": "https://github.com/viratkohli2011"
78
+ },
79
+ {
80
+ "name": "Nghia Minh Huynh",
81
+ "url": "https://github.com/NghiaMinhHuynh"
62
82
  }
63
83
  ],
64
84
  "license": "MIT",
@@ -67,15 +87,15 @@
67
87
  },
68
88
  "homepage": "https://github.com/vizhub-core/vzcode#readme",
69
89
  "dependencies": {
70
- "@codemirror/autocomplete": "^6.15.0",
90
+ "@codemirror/autocomplete": "^6.16.0",
71
91
  "@codemirror/lang-css": "^6.2.1",
72
- "@codemirror/lang-html": "^6.4.8",
92
+ "@codemirror/lang-html": "^6.4.9",
73
93
  "@codemirror/lang-javascript": "^6.2.2",
74
94
  "@codemirror/lang-json": "^6.0.1",
75
- "@codemirror/lang-markdown": "^6.2.4",
95
+ "@codemirror/lang-markdown": "^6.2.5",
76
96
  "@codemirror/state": "^6.4.1",
77
97
  "@codemirror/theme-one-dark": "^6.1.2",
78
- "@codemirror/view": "^6.26.2",
98
+ "@codemirror/view": "^6.26.3",
79
99
  "@lezer/highlight": "^1.2.0",
80
100
  "@replit/codemirror-indentation-markers": "^6.5.1",
81
101
  "@replit/codemirror-interact": "^6.3.1",
@@ -83,15 +103,15 @@
83
103
  "@replit/codemirror-vscode-keymap": "^6.0.2",
84
104
  "@teamwork/websocket-json-stream": "^2.0.0",
85
105
  "@typescript/vfs": "^1.5.0",
86
- "@uiw/codemirror-theme-abcdef": "^4.21.25",
87
- "@uiw/codemirror-theme-dracula": "^4.21.25",
88
- "@uiw/codemirror-theme-eclipse": "^4.21.25",
89
- "@uiw/codemirror-theme-github": "^4.21.25",
90
- "@uiw/codemirror-theme-material": "^4.21.25",
91
- "@uiw/codemirror-theme-nord": "^4.21.25",
92
- "@uiw/codemirror-theme-okaidia": "^4.21.25",
93
- "@uiw/codemirror-theme-xcode": "^4.21.25",
94
- "@uiw/codemirror-themes": "^4.21.25",
106
+ "@uiw/codemirror-theme-abcdef": "^4.22.0",
107
+ "@uiw/codemirror-theme-dracula": "^4.22.0",
108
+ "@uiw/codemirror-theme-eclipse": "^4.22.0",
109
+ "@uiw/codemirror-theme-github": "^4.22.0",
110
+ "@uiw/codemirror-theme-material": "^4.22.0",
111
+ "@uiw/codemirror-theme-nord": "^4.22.0",
112
+ "@uiw/codemirror-theme-okaidia": "^4.22.0",
113
+ "@uiw/codemirror-theme-xcode": "^4.22.0",
114
+ "@uiw/codemirror-themes": "^4.22.0",
95
115
  "body-parser": "^1.20.2",
96
116
  "codemirror": "^6.0.1",
97
117
  "codemirror-ot": "^4.4.0",
@@ -104,26 +124,26 @@
104
124
  "json0-ot-diff": "^1.1.2",
105
125
  "ngrok": "^4.3.3",
106
126
  "open": "^10.1.0",
107
- "openai": "^4.33.0",
108
- "prettier-plugin-svelte": "^3.2.2",
109
- "react": "^18.2.0",
127
+ "openai": "^4.40.0",
128
+ "prettier-plugin-svelte": "^3.2.3",
129
+ "react": "^18.3.1",
110
130
  "react-bootstrap": "^2.10.2",
111
- "react-dom": "^18.2.0",
112
- "react-router-dom": "^6.22.3",
131
+ "react-dom": "^18.3.1",
132
+ "react-router-dom": "^6.23.0",
113
133
  "sharedb": "^4.1.4",
114
134
  "sharedb-client-browser": "^4.5.0",
115
- "vizhub-ui": "^3.22.0",
116
- "ws": "^8.16.0"
135
+ "vizhub-ui": "^3.23.0",
136
+ "ws": "^8.17.0"
117
137
  },
118
138
  "devDependencies": {
119
- "@types/react": "^18.2.75",
120
- "@types/react-dom": "^18.2.24",
139
+ "@types/react": "^18.3.1",
140
+ "@types/react-dom": "^18.3.0",
121
141
  "@vitejs/plugin-react": "^4.2.1",
122
142
  "concurrently": "^8.2.2",
123
143
  "prettier": "^3.2.5",
124
- "sass": "^1.74.1",
125
- "typescript": "^5.4.4",
126
- "vite": "^5.2.8",
127
- "vitest": "^1.4.0"
144
+ "sass": "^1.76.0",
145
+ "typescript": "^5.4.5",
146
+ "vite": "^5.2.10",
147
+ "vitest": "^1.5.3"
128
148
  }
129
149
  }
@@ -0,0 +1,18 @@
1
+ // From https://heroicons.com/
2
+ export const QuestionMarkSVG = () => (
3
+ <svg
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ fill="none"
6
+ width="24"
7
+ height="24"
8
+ viewBox="0 0 24 24"
9
+ strokeWidth="1.5"
10
+ stroke="currentColor"
11
+ >
12
+ <path
13
+ strokeLinecap="round"
14
+ strokeLinejoin="round"
15
+ d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z"
16
+ />
17
+ </svg>
18
+ );
@@ -9,3 +9,4 @@ export { BugSVG } from './BugSVG';
9
9
  export { FileSVG } from './FileSVG';
10
10
  export { DirectoryArrowSVG } from './DirectoryArrowSVG';
11
11
  export { PlaySVG } from './PlaySVG';
12
+ export { QuestionMarkSVG } from './QuestionMarkSVG';
@@ -1,4 +1,4 @@
1
- import { useCallback, useContext } from 'react';
1
+ import { useCallback, useContext, useState } from 'react';
2
2
  import { PlaySVG } from '../Icons';
3
3
  import { VZCodeContext } from '../VZCodeContext';
4
4
  import { OverlayTrigger, Tooltip } from '../bootstrap';
@@ -16,8 +16,11 @@ export const RunCodeWidget = ({
16
16
  }) => {
17
17
  const { runCodeRef, runPrettierRef } =
18
18
  useContext(VZCodeContext);
19
+ const [isRunning, setIsRunning] = useState(false);
19
20
 
20
21
  const handleClick = useCallback(() => {
22
+ setIsRunning(true); // Set the running state to true
23
+
21
24
  // Run Prettier
22
25
  const runPrettier = runPrettierRef.current;
23
26
  if (runPrettier !== null) {
@@ -29,6 +32,9 @@ export const RunCodeWidget = ({
29
32
  if (runCode !== null) {
30
33
  runCode();
31
34
  }
35
+
36
+ // Optional: reset the icon state after animation completes (e.g., 1 second)
37
+ setTimeout(() => setIsRunning(false), 1000);
32
38
  }, []);
33
39
 
34
40
  return (
@@ -42,7 +48,7 @@ export const RunCodeWidget = ({
42
48
  }
43
49
  >
44
50
  <i
45
- className="icon-button icon-button-dark"
51
+ className={`icon-button icon-button-dark ${isRunning ? 'rotate-icon' : ''}`}
46
52
  onClick={handleClick}
47
53
  >
48
54
  <PlaySVG />
@@ -2,6 +2,18 @@
2
2
  position: absolute;
3
3
  right: 16px;
4
4
  top: 9px;
5
- // color: var(--vh-color-caution-01);
6
5
  color: var(--vh-color-neutral-04);
6
+
7
+ .rotate-icon {
8
+ animation: spin 1s linear;
9
+ }
10
+ }
11
+
12
+ @keyframes spin {
13
+ from {
14
+ transform: rotate(0deg);
15
+ }
16
+ to {
17
+ transform: rotate(360deg);
18
+ }
7
19
  }
@@ -30,7 +30,7 @@ const localStorageWriteDebounceMS = 800;
30
30
  const sidebarWidthProperty = 'vzCodeSidebarWidth';
31
31
  const codeEditorWidthProperty = 'vzCodeCodeEditorWidth';
32
32
 
33
- const initialSidebarWidthDefault = 220;
33
+ const initialSidebarWidthDefault = 250;
34
34
  let initialSidebarWidth: number =
35
35
  initialSidebarWidthDefault;
36
36
 
@@ -87,6 +87,10 @@ export type VZCodeContextValue = {
87
87
  setIsSettingsOpen: (isSettingsOpen: boolean) => void;
88
88
  closeSettings: () => void;
89
89
 
90
+ isDocOpen: boolean;
91
+ setIsDocOpen: (isDocOpen: boolean) => void;
92
+ closeDoc: () => void;
93
+
90
94
  theme: ThemeLabel;
91
95
  setTheme: (theme: ThemeLabel) => void;
92
96
 
@@ -204,6 +208,7 @@ export const VZCodeProvider = ({
204
208
  activeFileId,
205
209
  theme,
206
210
  isSettingsOpen,
211
+ isDocOpen,
207
212
  editorWantsFocus,
208
213
  username,
209
214
  } = state;
@@ -217,7 +222,9 @@ export const VZCodeProvider = ({
217
222
  closeTabs,
218
223
  setTheme,
219
224
  setIsSettingsOpen,
225
+ setIsDocOpen,
220
226
  closeSettings,
227
+ closeDoc,
221
228
  editorNoLongerWantsFocus,
222
229
  setUsername,
223
230
  } = useActions(dispatch);
@@ -344,6 +351,10 @@ export const VZCodeProvider = ({
344
351
  setIsSettingsOpen,
345
352
  closeSettings,
346
353
 
354
+ isDocOpen,
355
+ setIsDocOpen,
356
+ closeDoc,
357
+
347
358
  theme,
348
359
  setTheme,
349
360
 
@@ -0,0 +1,121 @@
1
+ import React, { useCallback, useContext } from 'react';
2
+ import { Button, Modal, Form } from './bootstrap';
3
+ import { ThemeLabel, themes } from './themes';
4
+ import { VZCodeContext } from './VZCodeContext';
5
+
6
+ export const VZKeyboardShortcutsDoc = ({
7
+ enableUsernameField = true,
8
+ }: {
9
+ // Feature flag to enable/disable username field
10
+ enableUsernameField?: boolean;
11
+ }) => {
12
+ const {
13
+ isDocOpen,
14
+ closeDoc,
15
+
16
+ setTheme,
17
+
18
+ setUsername,
19
+ } = useContext(VZCodeContext);
20
+
21
+ const handleThemeChange = useCallback(
22
+ (event: React.ChangeEvent<HTMLSelectElement>) => {
23
+ setTheme(event.target.value as ThemeLabel);
24
+ },
25
+ [],
26
+ );
27
+
28
+ return isDocOpen ? (
29
+ <Modal
30
+ className="vz-settings"
31
+ show={isDocOpen}
32
+ onHide={closeDoc}
33
+ animation={false}
34
+ >
35
+ <Modal.Header closeButton>
36
+ <Modal.Title>Keyboard Shortcuts</Modal.Title>
37
+ </Modal.Header>
38
+ <Modal.Body className="pt-1">
39
+ <Form.Group className="mb-3">
40
+ <Form.Text className="text-muted">
41
+ Keyboard shortcuts and interactive widgets for
42
+ quick actions
43
+ </Form.Text>
44
+ </Form.Group>
45
+ <Form.Group>
46
+ {/* <Form.Label>WINDOWS</Form.Label> */}
47
+ <ul>
48
+ <li>
49
+ <strong>Ctrl + s</strong> or{' '}
50
+ <strong>Shift + Enter</strong> <br />
51
+ <span>Run and format code</span>
52
+ </li>
53
+ <li>
54
+ <strong>Alt + drag on a number</strong>
55
+ <br />
56
+ <span>
57
+ Modify the number by dragging left or right
58
+ </span>
59
+ </li>
60
+ <li>
61
+ <strong>Alt + click on a hex color</strong>
62
+ <br />
63
+ <span>
64
+ Open a color picker to modify the color
65
+ </span>
66
+ </li>
67
+ <li>
68
+ <strong>Alt + w</strong> <br />
69
+ <span>Close the current tab</span>
70
+ </li>
71
+ <li>
72
+ <strong>Alt + n</strong> <br />
73
+ <span>Open the create file modal</span>
74
+ </li>
75
+ <li>
76
+ <strong>Alt + Page Up</strong> <br />
77
+ <span>
78
+ Change the active tab to the previous one
79
+ </span>
80
+ </li>
81
+ <li>
82
+ <strong>Alt + Page Down</strong> <br />
83
+ <span>
84
+ Change the active tab to the next one
85
+ </span>
86
+ </li>
87
+ <li>
88
+ <strong>Most VSCode Shortcuts</strong> <br />
89
+ See VSCode docs:{' '}
90
+ <a
91
+ href="https://code.visualstudio.com/shortcuts/keyboard-shortcuts-windows.pdf"
92
+ target="_blank"
93
+ >
94
+ Windows
95
+ </a>
96
+ ,{' '}
97
+ <a
98
+ href="https://code.visualstudio.com/shortcuts/keyboard-shortcuts-macos.pdf"
99
+ target="_blank"
100
+ >
101
+ MacOS
102
+ </a>
103
+ ,{' '}
104
+ <a
105
+ href="https://code.visualstudio.com/shortcuts/keyboard-shortcuts-linux.pdf"
106
+ target="_blank"
107
+ >
108
+ Linux
109
+ </a>
110
+ </li>
111
+ </ul>
112
+ </Form.Group>
113
+ </Modal.Body>
114
+ <Modal.Footer>
115
+ <Button variant="primary" onClick={closeDoc}>
116
+ Done
117
+ </Button>
118
+ </Modal.Footer>
119
+ </Modal>
120
+ ) : null;
121
+ };
@@ -1,4 +1,5 @@
1
1
  import { VZSettings } from './VZSettings';
2
+ import { VZKeyboardShortcutsDoc } from './VZKeyboardShortcutsDoc';
2
3
  import { VZSidebar } from './VZSidebar';
3
4
  import { CreateFileModal } from './VZSidebar/CreateFileModal';
4
5
  import { CreateDirModal } from './VZSidebar/CreateDirModal';
@@ -14,6 +15,9 @@ export const VZLeft = ({ enableUsernameField = true }) => {
14
15
  <VZSettings
15
16
  enableUsernameField={enableUsernameField}
16
17
  />
18
+ <VZKeyboardShortcutsDoc
19
+ enableUsernameField={enableUsernameField}
20
+ />
17
21
  <CreateFileModal />
18
22
  <CreateDirModal />
19
23
  </div>
@@ -116,6 +116,25 @@ export const VZSettings = ({
116
116
  setUsername(usernameRef.current?.value || '');
117
117
  }, [setUsername]);
118
118
 
119
+ // Function to handle pressing Enter key
120
+ useEffect(() => {
121
+ if (isSettingsOpen) {
122
+ const handleEnterKey = (event: KeyboardEvent) => {
123
+ if (event.key === 'Enter') {
124
+ closeSettings();
125
+ }
126
+ };
127
+
128
+ window.addEventListener('keydown', handleEnterKey);
129
+ return () => {
130
+ window.removeEventListener(
131
+ 'keydown',
132
+ handleEnterKey,
133
+ );
134
+ };
135
+ }
136
+ }, [isSettingsOpen, closeSettings]);
137
+
119
138
  return isSettingsOpen ? (
120
139
  <Modal
121
140
  className="vz-settings"
@@ -164,6 +183,7 @@ export const VZSettings = ({
164
183
 
165
184
  <Form.Group className="mb-3" controlId="formFork">
166
185
  <Form.Label>Font</Form.Label>
186
+
167
187
  <select
168
188
  className="form-select"
169
189
  onChange={handleFontChange}
@@ -175,6 +195,7 @@ export const VZSettings = ({
175
195
  </option>
176
196
  ))}
177
197
  </select>
198
+
178
199
  <Form.Text className="text-muted">
179
200
  Select font for the editor
180
201
  </Form.Text>
@@ -182,6 +203,7 @@ export const VZSettings = ({
182
203
 
183
204
  <Form.Group className="mb-3" controlId="formFork">
184
205
  <Form.Label>Font Size</Form.Label>
206
+
185
207
  <select
186
208
  className="form-select"
187
209
  onChange={handleFontSizeChange}
@@ -193,51 +215,9 @@ export const VZSettings = ({
193
215
  </option>
194
216
  ))}
195
217
  </select>
196
- <Form.Text className="text-muted">
197
- Select a font size for the editor
198
- </Form.Text>
199
- </Form.Group>
200
218
 
201
- {/* Keyboard Shortcuts Documentation */}
202
- <Form.Group className="mb-3" controlId="formFork">
203
- <Form.Label>Keyboard Shortcuts</Form.Label>
204
- <ul>
205
- <li>
206
- <strong>Alt-w</strong> <span>⌥W</span>
207
- <br />
208
- <span>Close the current tab</span>
209
- </li>
210
- <li>
211
- <strong>Alt-n</strong> <span>⌥N</span>
212
- <br />
213
- <span>Open the create file modal</span>
214
- </li>
215
- <li>
216
- <strong>Alt-PageUp</strong> <span>⌥⇞</span>
217
- <br />
218
- <span>
219
- Change the active tab to the previous one
220
- </span>
221
- </li>
222
- <li>
223
- <strong>Alt-PageDown</strong> <span>⌥⇟</span>
224
- <br />
225
- <span>
226
- Change the active tab to the next one
227
- </span>
228
- </li>
229
- <li>
230
- <strong>Ctrl-s</strong> or{' '}
231
- <strong>Shift-Enter</strong> <span>⌘S</span>{' '}
232
- or <span>⇧↩</span>
233
- <br />
234
- <span>
235
- Run the code and format it with Prettier
236
- </span>
237
- </li>
238
- </ul>
239
219
  <Form.Text className="text-muted">
240
- Keyboard shortcuts for quick actions
220
+ Select a font size for the editor
241
221
  </Form.Text>
242
222
  </Form.Group>
243
223
  </Modal.Body>
@@ -15,6 +15,7 @@ export const CreateDirModal = ({
15
15
  const inputRef = useRef(null);
16
16
 
17
17
  const {
18
+ files,
18
19
  isCreateDirModalOpen,
19
20
  handleCloseCreateDirModal,
20
21
  handleCreateDirClick,
@@ -38,11 +39,22 @@ export const CreateDirModal = ({
38
39
  // Returns true if file name is valid, false otherwise.
39
40
  const validateFileName = useCallback(
40
41
  (fileName: string) => {
42
+ let valid;
43
+ //General Character Check
41
44
  const regex =
42
45
  /^[a-zA-Z0-9](?:[a-zA-Z0-9 ./+=_-]*[a-zA-Z0-9])?$/;
43
- return regex.test(fileName);
46
+ valid = regex.test(fileName);
47
+
48
+ //Check for Duplicate Filename
49
+ for (const key in files) {
50
+ if (fileName + '/' === files[key].name) {
51
+ valid = false;
52
+ }
53
+ }
54
+
55
+ return valid;
44
56
  },
45
- [],
57
+ [files],
46
58
  );
47
59
 
48
60
  const handleKeyDown = useCallback(
@@ -15,6 +15,7 @@ export const CreateFileModal = ({
15
15
  const inputRef = useRef(null);
16
16
 
17
17
  const {
18
+ files,
18
19
  isCreateFileModalOpen,
19
20
  handleCloseCreateFileModal,
20
21
  handleCreateFileClick,
@@ -38,11 +39,22 @@ export const CreateFileModal = ({
38
39
  // Returns true if file name is valid, false otherwise.
39
40
  const validateFileName = useCallback(
40
41
  (fileName: string) => {
42
+ let valid;
43
+ //General Character Check
41
44
  const regex =
42
45
  /^[a-zA-Z0-9](?:[a-zA-Z0-9 ./+=_-]*[a-zA-Z0-9])?$/;
43
- return regex.test(fileName);
46
+ valid = regex.test(fileName);
47
+
48
+ //Check for Duplicate Filename
49
+ for (const key in files) {
50
+ if (fileName === files[key].name) {
51
+ valid = false;
52
+ }
53
+ }
54
+
55
+ return valid;
44
56
  },
45
- [],
57
+ [files],
46
58
  );
47
59
 
48
60
  const handleKeyDown = useCallback(