vzcode 0.82.0 → 0.84.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,7 +20,7 @@
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-BKQfM_WG.js"></script>
23
+ <script type="module" crossorigin src="/assets/index-9InCi-kT.js"></script>
24
24
  <link rel="stylesheet" crossorigin href="/assets/index-J8-kRnnE.css">
25
25
  </head>
26
26
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vzcode",
3
- "version": "0.82.0",
3
+ "version": "0.84.0",
4
4
  "description": "Multiplayer code editor system",
5
5
  "main": "src/index.ts",
6
6
  "type": "module",
@@ -55,6 +55,10 @@
55
55
  {
56
56
  "name": "Evan Whymark",
57
57
  "url": "https://github.com/EvWhymark"
58
+ },
59
+ {
60
+ "name": "Ethan Nuessle",
61
+ "url": "https://github.com/enuessle"
58
62
  }
59
63
  ],
60
64
  "license": "MIT",
@@ -71,7 +75,7 @@
71
75
  "@codemirror/lang-markdown": "^6.2.4",
72
76
  "@codemirror/state": "^6.4.1",
73
77
  "@codemirror/theme-one-dark": "^6.1.2",
74
- "@codemirror/view": "^6.26.0",
78
+ "@codemirror/view": "^6.26.1",
75
79
  "@lezer/highlight": "^1.2.0",
76
80
  "@replit/codemirror-indentation-markers": "^6.5.1",
77
81
  "@replit/codemirror-interact": "^6.3.1",
@@ -100,26 +104,26 @@
100
104
  "json0-ot-diff": "^1.1.2",
101
105
  "ngrok": "^4.3.3",
102
106
  "open": "^10.1.0",
103
- "openai": "^4.29.2",
107
+ "openai": "^4.31.0",
104
108
  "prettier-plugin-svelte": "^3.2.2",
105
109
  "react": "^18.2.0",
106
110
  "react-bootstrap": "^2.10.2",
107
111
  "react-dom": "^18.2.0",
108
112
  "react-router-dom": "^6.22.3",
109
- "sharedb": "^4.1.2",
113
+ "sharedb": "^4.1.3",
110
114
  "sharedb-client-browser": "^4.4.0",
111
115
  "vizhub-ui": "^3.21.0",
112
116
  "ws": "^8.16.0"
113
117
  },
114
118
  "devDependencies": {
115
- "@types/react": "^18.2.72",
116
- "@types/react-dom": "^18.2.22",
119
+ "@types/react": "^18.2.73",
120
+ "@types/react-dom": "^18.2.23",
117
121
  "@vitejs/plugin-react": "^4.2.1",
118
122
  "concurrently": "^8.2.2",
119
123
  "prettier": "^3.2.5",
120
124
  "sass": "^1.72.0",
121
125
  "typescript": "^5.4.3",
122
- "vite": "^5.2.6",
126
+ "vite": "^5.2.7",
123
127
  "vitest": "^1.4.0"
124
128
  }
125
129
  }
@@ -92,7 +92,7 @@ function App() {
92
92
  >
93
93
  <div className="app">
94
94
  <VZLeft />
95
- <VZMiddle />
95
+ <VZMiddle allowGlobals={true} />
96
96
  {enableRightPanel ? <VZRight /> : null}
97
97
  <VZResizer side="left" />
98
98
  {enableRightPanel ? (
@@ -1,11 +1,6 @@
1
- // TODO move back to this way of importing when this PR is merged:
2
- // https://github.com/replit/codemirror-interact/pull/19/files
3
1
  import interact, {
4
2
  InteractRule,
5
3
  } from '@replit/codemirror-interact';
6
- // import interact, {
7
- // InteractRule,
8
- // } from './codemirror-interact';
9
4
 
10
5
  import {
11
6
  ViewPlugin,
@@ -58,6 +53,7 @@ export const widgets = ({
58
53
  const updateHex = (e: Event) => {
59
54
  const el: HTMLInputElement =
60
55
  e.target as HTMLInputElement;
56
+ if (onInteract) onInteract();
61
57
  if (el.value) {
62
58
  setText(
63
59
  valueIsUpper
@@ -49,6 +49,7 @@ export const CreateFileModal = ({
49
49
  (e) => {
50
50
  if (
51
51
  e.key === 'Enter' &&
52
+ validateFileName(newName) &&
52
53
  (e.ctrlKey ||
53
54
  e.shiftKey ||
54
55
  (!e.altKey && !e.metaKey))
@@ -56,7 +57,7 @@ export const CreateFileModal = ({
56
57
  onCreateClick();
57
58
  }
58
59
  },
59
- [onCreateClick],
60
+ [onCreateClick, validateFileName, newName],
60
61
  );
61
62
 
62
63
  return isCreateFileModalOpen ? (
@@ -29,6 +29,7 @@ import {
29
29
  GOLDENROD,
30
30
  PANIC,
31
31
  } from './colors';
32
+ import { EditorView } from 'codemirror';
32
33
 
33
34
  // Use semi-bold weight for keywords and other important things
34
35
  // const boldWeight = 'bold';
@@ -57,116 +58,142 @@ function vizhubThemeInit(
57
58
  settings = {},
58
59
  styles = [],
59
60
  } = options || {};
60
- return createTheme({
61
- theme: theme,
62
- settings: {
63
- ...defaultSettingsVizhubTheme,
64
- ...settings,
65
- },
66
- styles: [
67
- {
68
- tag: [
69
- t.keyword,
70
- t.operatorKeyword,
71
- t.modifier,
72
- t.color,
73
- t.constant(t.name),
74
- t.standard(t.name),
75
- t.standard(t.tagName),
76
- t.special(t.brace),
77
- t.atom,
78
- t.bool,
79
- t.special(t.variableName),
80
- ],
81
- color: MINT,
82
- },
83
- {
84
- tag: [t.controlKeyword, t.moduleKeyword],
85
- color: MINT,
61
+ return [
62
+ createTheme({
63
+ theme: theme,
64
+ settings: {
65
+ ...defaultSettingsVizhubTheme,
66
+ ...settings,
86
67
  },
87
- {
88
- tag: [
89
- t.name,
90
- t.deleted,
91
- t.character,
92
- t.macroName,
68
+ styles: [
69
+ {
70
+ tag: [
71
+ t.keyword,
72
+ t.operatorKeyword,
73
+ t.modifier,
74
+ t.color,
75
+ t.constant(t.name),
76
+ t.standard(t.name),
77
+ t.standard(t.tagName),
78
+ t.special(t.brace),
79
+ t.atom,
80
+ t.bool,
81
+ t.special(t.variableName),
82
+ ],
83
+ color: MINT,
84
+ },
85
+ {
86
+ tag: [t.controlKeyword, t.moduleKeyword],
87
+ color: MINT,
88
+ },
89
+ {
90
+ tag: [
91
+ t.name,
92
+ t.deleted,
93
+ t.character,
94
+ t.macroName,
93
95
 
94
- t.variableName,
95
- t.labelName,
96
- t.definition(t.name),
97
- ],
98
- color: AQUA,
99
- },
100
- {
101
- tag: [t.propertyName],
102
- color: GOLDENROD,
103
- },
104
- {
105
- tag: t.heading,
106
- fontWeight: boldWeight,
107
- color: SKY,
108
- },
109
- {
110
- tag: [
111
- t.typeName,
112
- t.className,
113
- t.tagName,
114
- t.number,
115
- t.changed,
116
- t.annotation,
117
- t.self,
118
- t.namespace,
119
- ],
120
- color: AQUA,
121
- },
122
- {
123
- tag: [
124
- t.function(t.variableName),
125
- t.function(t.propertyName),
126
- ],
127
- color: AQUA,
128
- },
129
- { tag: [t.number], color: SALMON },
130
- {
131
- tag: [
132
- t.operator,
133
- t.punctuation,
134
- t.separator,
135
- t.url,
136
- t.escape,
137
- t.regexp,
138
- ],
139
- color: light,
140
- },
141
- {
142
- tag: [t.regexp],
143
- color: LAVENDER,
144
- },
145
- {
146
- tag: [t.special(t.string), t.string, t.inserted],
147
- color: LAVENDER,
148
- },
149
- {
150
- tag: [t.processingInstruction],
151
- color: AQUA,
152
- },
153
- { tag: [t.angleBracket], color: AQUA },
154
- { tag: t.strong, fontWeight: boldWeight },
155
- { tag: t.emphasis, fontStyle: 'italic' },
156
- {
157
- tag: t.strikethrough,
158
- textDecoration: 'line-through',
159
- },
160
- { tag: [t.meta, t.comment], color: dark },
96
+ t.variableName,
97
+ t.labelName,
98
+ t.definition(t.name),
99
+ ],
100
+ color: AQUA,
101
+ },
102
+ {
103
+ tag: [t.propertyName],
104
+ color: GOLDENROD,
105
+ },
106
+ {
107
+ tag: t.heading,
108
+ fontWeight: boldWeight,
109
+ color: SKY,
110
+ },
111
+ {
112
+ tag: [
113
+ t.typeName,
114
+ t.className,
115
+ t.tagName,
116
+ t.number,
117
+ t.changed,
118
+ t.annotation,
119
+ t.self,
120
+ t.namespace,
121
+ ],
122
+ color: AQUA,
123
+ },
124
+ {
125
+ tag: [
126
+ t.function(t.variableName),
127
+ t.function(t.propertyName),
128
+ ],
129
+ color: AQUA,
130
+ },
131
+ { tag: [t.number], color: SALMON },
132
+ {
133
+ tag: [
134
+ t.operator,
135
+ t.punctuation,
136
+ t.separator,
137
+ t.url,
138
+ t.escape,
139
+ t.regexp,
140
+ ],
141
+ color: light,
142
+ },
143
+ {
144
+ tag: [t.regexp],
145
+ color: LAVENDER,
146
+ },
147
+ {
148
+ tag: [t.special(t.string), t.string, t.inserted],
149
+ color: LAVENDER,
150
+ },
151
+ {
152
+ tag: [t.processingInstruction],
153
+ color: AQUA,
154
+ },
155
+ { tag: [t.angleBracket], color: AQUA },
156
+ { tag: t.strong, fontWeight: boldWeight },
157
+ { tag: t.emphasis, fontStyle: 'italic' },
158
+ {
159
+ tag: t.strikethrough,
160
+ textDecoration: 'line-through',
161
+ },
162
+ { tag: [t.meta, t.comment], color: dark },
163
+ {
164
+ tag: t.link,
165
+ color: GOLDENROD,
166
+ textDecoration: 'underline',
167
+ },
168
+ { tag: t.invalid, color: PANIC },
169
+ // {
170
+ // class: 'cm-nonmatchingBracket',
171
+ // // color: "rede",
172
+ // backgroundColor: 'red', // Optional: set background color for matching brackets
173
+ // },
174
+ // {
175
+ // tag: t.nonmatchingBracket,
176
+ // color: nonMatchingBracketColor,
177
+ // backgroundColor: 'transparent', // Optional: set background color for non-matching brackets
178
+ // },
179
+ ...styles,
180
+ ],
181
+ }),
182
+ // Theme matching and non-matching brackets.
183
+ EditorView.theme(
161
184
  {
162
- tag: t.link,
163
- color: GOLDENROD,
164
- textDecoration: 'underline',
185
+ '&.cm-focused .cm-matchingBracket': {
186
+ backgroundColor: 'transparent',
187
+ outline: '1px solid #888888',
188
+ },
189
+ '&.cm-focused .cm-nonmatchingBracket': {
190
+ backgroundColor: 'transparent',
191
+ outline: '1px solid #ff2222',
192
+ },
165
193
  },
166
- { tag: t.invalid, color: PANIC },
167
- ...styles,
168
- ],
169
- });
194
+ { dark: true },
195
+ ),
196
+ ];
170
197
  }
171
198
 
172
199
  export const vizhubTheme = vizhubThemeInit();
@@ -0,0 +1,73 @@
1
+ import ts from 'typescript';
2
+
3
+ export const compilerOptions: ts.CompilerOptions = {
4
+ lib: ['dom', 'esnext'],
5
+
6
+ // Disable warnings around "Any type".
7
+ noImplicitAny: false,
8
+
9
+ // Disable warnings around "Implicit any in parameter".
10
+ noImplicitThis: false,
11
+
12
+ // Allow JavaScript files to be processed
13
+ allowJs: true,
14
+
15
+ // Disable type checking for JavaScript files
16
+ // checkJs: false,
17
+
18
+ // Skip type checking of declaration files
19
+ skipLibCheck: true,
20
+
21
+ // Support React JSX
22
+ jsx: ts.JsxEmit.React,
23
+ };
24
+
25
+ // Be less aggressive for non-TS files,
26
+ // e.g. files that end in .js or .jsx.
27
+ // if (!isTS(fileName)) {
28
+ // This code is for errors like:
29
+ // "Binding element 'data' implicitly has an 'any' type."
30
+ const LINT_ERROR_CODE_ANY = 7031;
31
+
32
+ // This code is for errors like:
33
+ // "Parameter 'selection' implicitly has an 'any' type.""
34
+ const LINT_ERROR_CODE_ANY_PARAM = 7006;
35
+
36
+ // This code is for errors like:
37
+ // "Cannot find module 'd3' or its corresponding type declarations."
38
+ const LINT_ERROR_CODE_IMPORT = 2307;
39
+
40
+ // This code is for errors like:
41
+ // "Variable 'mic' implicitly has type 'any' in some locations where its type cannot be determined."
42
+ const LINT_ERROR_CODE_ANY_TYPE = 7034;
43
+
44
+ // "Element implicitly has an 'any' type because expression
45
+ // of type '"test"' can't be used to index type '{}'."
46
+ const LINT_ERROR_CODE_ANY_TYPE_KEYS = 7053;
47
+
48
+ // "Property 'id' does not exist on type { ... }"
49
+ // Happens on objects with dynamic keys.
50
+ // Not valid in TypeScript, but common in JavaScript.
51
+ const LINT_ERROR_CODE_NON_EXISTENT_PROPERTY = 2339;
52
+
53
+ // "Type 'Set<unknown>' can only be iterated through when using the '--downlevelIteration'
54
+ // flag or with a '--target' of 'es2015' or higher."
55
+ const LINT_ERROR_CODE_ITERATED_THROUGH = 2802;
56
+
57
+ // Argument of type '{ Month: string; High: number; Temp: number; Low: number; }'
58
+ // is not assignable to parameter of type 'never'.
59
+ const LINT_ERROR_CODE_ASSIGNABLE_TO_NEVER = 2345;
60
+
61
+ // Cannot find name 'd3'.
62
+ export const LINT_ERROR_CODE_CANNOT_FIND_NAME = 2304;
63
+
64
+ export const excludedErrorCodes = new Set([
65
+ LINT_ERROR_CODE_ANY,
66
+ LINT_ERROR_CODE_IMPORT,
67
+ LINT_ERROR_CODE_ANY_PARAM,
68
+ LINT_ERROR_CODE_ANY_TYPE,
69
+ LINT_ERROR_CODE_ANY_TYPE_KEYS,
70
+ LINT_ERROR_CODE_NON_EXISTENT_PROPERTY,
71
+ LINT_ERROR_CODE_ITERATED_THROUGH,
72
+ LINT_ERROR_CODE_ASSIGNABLE_TO_NEVER,
73
+ ]);
@@ -0,0 +1,4 @@
1
+ // replace .js or .jsx with .ts or .tsx,
2
+ // to support TypeScript completions on non-TS files.
3
+ export const getTSFileName = (fileName: string) =>
4
+ fileName.replace(/\.jsx?$/, '.tsx');
@@ -0,0 +1,63 @@
1
+ import { getTSFileName } from './getTSFileName';
2
+ import { isTS } from './isTS';
3
+ import {
4
+ AutocompleteRequest,
5
+ AutocompleteResponse,
6
+ } from './requestTypes';
7
+
8
+ // This function is called when the worker receives a message with the type
9
+ // 'autocomplete-request'. It gets completions at the specified position in the
10
+ // file and sends them back to the main thread.
11
+ export const handleMessageAutocompleteRequest = async ({
12
+ debug,
13
+ data,
14
+ env,
15
+ setFile,
16
+ }) => {
17
+ if (debug) {
18
+ console.log('autocomplete-request message received');
19
+ }
20
+
21
+ // Should not happen.
22
+ if (env === null) {
23
+ console.log('env is null');
24
+ return;
25
+ }
26
+
27
+ // Example of `data`:
28
+ // {
29
+ // "event": "autocomplete-request",
30
+ // "pos": 8,
31
+ // "location": "index.js",
32
+ // "requestId": "0.9090605799171392"
33
+ // }
34
+
35
+ const autocompleteRequest: AutocompleteRequest = data;
36
+ const { fileName, fileContent, position, requestId } =
37
+ autocompleteRequest;
38
+
39
+ const tsFileName = getTSFileName(fileName);
40
+
41
+ let completions = null;
42
+ if (isTS(tsFileName) && fileContent !== '') {
43
+ // Update the file in the file system to the
44
+ // absolute latest version. This is critical
45
+ // for correct completions.
46
+ setFile(tsFileName, fileContent);
47
+
48
+ completions =
49
+ env.languageService.getCompletionsAtPosition(
50
+ tsFileName,
51
+ position,
52
+ {},
53
+ );
54
+ }
55
+
56
+ const autocompleteResponse: AutocompleteResponse = {
57
+ event: 'post-completions',
58
+ completions,
59
+ requestId,
60
+ };
61
+
62
+ postMessage(autocompleteResponse);
63
+ };
@@ -0,0 +1,84 @@
1
+ import ts from 'typescript';
2
+ import type { Diagnostic } from '@codemirror/lint';
3
+ import {
4
+ LinterRequest,
5
+ LinterResponse,
6
+ } from './requestTypes';
7
+ import { getTSFileName } from './getTSFileName';
8
+ import { isTS } from './isTS';
9
+ import {
10
+ LINT_ERROR_CODE_CANNOT_FIND_NAME,
11
+ excludedErrorCodes,
12
+ } from './constants';
13
+
14
+ // Inspired by: https://stackblitz.com/edit/codemirror-6-typescript?file=client%2Findex.ts%3AL44-L44
15
+ const convertToCodeMirrorDiagnostic = (
16
+ tsErrors: ts.Diagnostic[],
17
+ ): Array<Diagnostic> =>
18
+ tsErrors.map((tsError: ts.Diagnostic) => ({
19
+ from: tsError.start,
20
+ to: tsError.start + tsError.length,
21
+ severity: 'error',
22
+ message:
23
+ typeof tsError.messageText === 'string'
24
+ ? tsError.messageText
25
+ : tsError.messageText.messageText,
26
+ }));
27
+
28
+ export const handleMessageLintRequest = async ({
29
+ debug,
30
+ data,
31
+ env,
32
+ setFile,
33
+ }) => {
34
+ if (debug) {
35
+ console.log('Lint Request');
36
+ }
37
+ const linterRequest: LinterRequest = data;
38
+ const { fileName, fileContent, requestId, allowGlobals } =
39
+ linterRequest;
40
+
41
+ const tsFileName = getTSFileName(fileName);
42
+ let tsErrors = [];
43
+ if (isTS(tsFileName) && fileContent !== '') {
44
+ // We are updating the server with the latest content
45
+ // when we autocomplete, so it's always up to date.
46
+ setFile(tsFileName, fileContent);
47
+
48
+ // Creates an array of diagnostic objects containing
49
+ // both semantic and syntactic diagnostics.
50
+ tsErrors = env.languageService
51
+ .getSemanticDiagnostics(tsFileName)
52
+ .concat(
53
+ env.languageService.getSyntacticDiagnostics(
54
+ tsFileName,
55
+ ),
56
+ );
57
+
58
+ if (debug) {
59
+ console.log('tsErrors');
60
+ console.log(tsErrors);
61
+ }
62
+
63
+ tsErrors = tsErrors.filter(
64
+ (error: { code: number }) => {
65
+ if (
66
+ allowGlobals &&
67
+ error.code === LINT_ERROR_CODE_CANNOT_FIND_NAME
68
+ ) {
69
+ return false;
70
+ }
71
+ return !excludedErrorCodes.has(error.code);
72
+ },
73
+ );
74
+
75
+ tsErrors = convertToCodeMirrorDiagnostic(tsErrors);
76
+ }
77
+
78
+ const linterResponse: LinterResponse = {
79
+ event: 'post-error-linter',
80
+ tsErrors,
81
+ requestId,
82
+ };
83
+ postMessage(linterResponse);
84
+ };
@@ -0,0 +1,20 @@
1
+ import ts from 'typescript';
2
+ import { compilerOptions } from './constants';
3
+
4
+ // Handle the transpile-request event, which
5
+ // transpiles TypeScript to JavaScript.
6
+ export const handleMessageTranspileRequest = async ({
7
+ data,
8
+ }) => {
9
+ const tsCode = data.tsCode;
10
+
11
+ const jsCode = ts.transpileModule(tsCode, {
12
+ compilerOptions: compilerOptions,
13
+ }).outputText;
14
+
15
+ postMessage({
16
+ event: 'transpile-response',
17
+ jsCode,
18
+ fileId: data.fileId,
19
+ });
20
+ };
@@ -0,0 +1,38 @@
1
+ import { File, Files, VZCodeContent } from '../../../types';
2
+ import { getTSFileName } from './getTSFileName';
3
+ import { isTS } from './isTS';
4
+
5
+ // Handle the update-content event, which
6
+ // updates the files as they change.
7
+ // This handles the cases where:
8
+ // * The file system is populated for the first time.
9
+ // * Files are edited by remote users.
10
+ export const handleMessageUpdateContent = async ({
11
+ debug,
12
+ data,
13
+ setFile,
14
+ }) => {
15
+ if (debug) {
16
+ console.log('update-content message received');
17
+ }
18
+ // Unpack the files
19
+ const content: VZCodeContent = data.details;
20
+ const files: Files = content.files;
21
+
22
+ // Iterate over the files
23
+ for (const fileId of Object.keys(files)) {
24
+ const file: File = files[fileId];
25
+ const { name, text } = file;
26
+
27
+ const tsFileName = getTSFileName(name);
28
+
29
+ if (!isTS(tsFileName)) {
30
+ continue;
31
+ }
32
+
33
+ setFile(tsFileName, text);
34
+ // TODO - Handle renaming files.
35
+ // TODO - Handle deleting files.
36
+ // TODO - Handle directories.
37
+ }
38
+ };