vzcode 1.30.1 → 1.32.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 (38) hide show
  1. package/dist/assets/{index-zzK6rRiK.css → index-DTtcN2MP.css} +1 -1
  2. package/dist/assets/index-sreAszZh.js +240 -0
  3. package/dist/assets/worker-BKfYWklR.js +136 -0
  4. package/dist/assets/{index-Dd8gk_1S.js → worker-COyDRdqW.js} +109 -103
  5. package/dist/assets/{worker-CaP6zYz7.js → worker-CWsWXMyk.js} +70 -70
  6. package/dist/index.html +2 -2
  7. package/package.json +42 -36
  8. package/src/client/AIAssist/startAIAssist.ts +0 -24
  9. package/src/client/App/index.tsx +14 -7
  10. package/src/client/CodeEditor/getOrCreateEditor.ts +80 -56
  11. package/src/client/CodeEditor/index.tsx +56 -30
  12. package/src/client/CodeEditor/typescriptExtension/worker.ts +32 -0
  13. package/src/client/RunCodeWidget/index.tsx +0 -2
  14. package/src/client/VZCodeContext.tsx +8 -25
  15. package/src/client/VZMiddle.tsx +11 -5
  16. package/src/client/VZSidebar/Search.tsx +11 -0
  17. package/src/client/VZSidebar/index.tsx +18 -11
  18. package/src/client/VZSidebar/styles.scss +27 -7
  19. package/src/client/VZSidebar/useDragAndDrop.tsx +237 -35
  20. package/src/client/main.jsx +0 -2
  21. package/src/client/useESLint/index.ts +72 -0
  22. package/src/client/useESLint/worker.ts +113 -0
  23. package/src/client/useFileCRUD.ts +0 -2
  24. package/src/server/featureFlags.js +4 -6
  25. package/src/server/setupEnv.js +0 -10
  26. package/dist/assets/index-BOiH3_fC.js +0 -234
  27. package/src/client/CodeEditor/typeScriptCompletions.ts +0 -134
  28. package/src/client/CodeEditor/typeScriptLinter.ts +0 -47
  29. package/src/client/useTypeScript/index.ts +0 -48
  30. package/src/client/useTypeScript/worker/constants.ts +0 -94
  31. package/src/client/useTypeScript/worker/getTSFileName.ts +0 -4
  32. package/src/client/useTypeScript/worker/handleMessageAutocompleteRequest.ts +0 -63
  33. package/src/client/useTypeScript/worker/handleMessageLintRequest.ts +0 -84
  34. package/src/client/useTypeScript/worker/handleMessageTranspileRequest.ts +0 -20
  35. package/src/client/useTypeScript/worker/handleMessageUpdateContent.ts +0 -42
  36. package/src/client/useTypeScript/worker/index.ts +0 -110
  37. package/src/client/useTypeScript/worker/isTS.ts +0 -3
  38. package/src/client/useTypeScript/worker/requestTypes.ts +0 -31
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-BOiH3_fC.js"></script>
24
- <link rel="stylesheet" crossorigin href="/assets/index-zzK6rRiK.css">
23
+ <script type="module" crossorigin src="/assets/index-sreAszZh.js"></script>
24
+ <link rel="stylesheet" crossorigin href="/assets/index-DTtcN2MP.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.30.1",
3
+ "version": "1.32.0",
4
4
  "description": "Multiplayer code editor system",
5
5
  "main": "src/index.ts",
6
6
  "type": "module",
@@ -13,7 +13,7 @@
13
13
  "test": "vitest run",
14
14
  "test-interactive": "cd test/sampleDirectories/kitchenSink && node ../../../src/server/index.js",
15
15
  "prettier": "prettier {*.*,**/*.*} --write",
16
- "tsc": "tsc --noEmit",
16
+ "typecheck": "tsc --noEmit",
17
17
  "dev": "cross-env EDITOR_PORT=5173 concurrently \"npm run test-interactive\" \"vite\"",
18
18
  "build": "vite build",
19
19
  "build-release": "vite build --mode release",
@@ -107,73 +107,79 @@
107
107
  "@codemirror/autocomplete": "^6.18.6",
108
108
  "@codemirror/lang-css": "^6.3.1",
109
109
  "@codemirror/lang-html": "^6.4.9",
110
- "@codemirror/lang-javascript": "^6.2.3",
110
+ "@codemirror/lang-javascript": "^6.2.4",
111
111
  "@codemirror/lang-json": "^6.0.1",
112
112
  "@codemirror/lang-markdown": "^6.3.2",
113
+ "@codemirror/lint": "^6.8.5",
113
114
  "@codemirror/state": "^6.5.2",
114
115
  "@codemirror/theme-one-dark": "^6.1.2",
115
- "@codemirror/view": "^6.36.5",
116
- "@langchain/core": "^0.3.43",
117
- "@langchain/openai": "^0.5.2",
116
+ "@codemirror/view": "^6.37.1",
117
+ "@langchain/core": "^0.3.57",
118
+ "@langchain/openai": "^0.5.12",
118
119
  "@lezer/highlight": "^1.2.1",
119
- "@livekit/components-react": "^2.9.0",
120
+ "@livekit/components-react": "^2.9.9",
120
121
  "@replit/codemirror-indentation-markers": "^6.5.3",
121
122
  "@replit/codemirror-interact": "^6.3.1",
122
123
  "@replit/codemirror-lang-svelte": "^6.0.0",
123
124
  "@replit/codemirror-vscode-keymap": "^6.0.2",
124
125
  "@teamwork/websocket-json-stream": "^2.0.0",
125
126
  "@typescript/vfs": "^1.6.1",
126
- "@uiw/codemirror-theme-abcdef": "^4.23.10",
127
- "@uiw/codemirror-theme-dracula": "^4.23.10",
128
- "@uiw/codemirror-theme-eclipse": "^4.23.10",
129
- "@uiw/codemirror-theme-github": "^4.23.10",
130
- "@uiw/codemirror-theme-material": "^4.23.10",
131
- "@uiw/codemirror-theme-nord": "^4.23.10",
132
- "@uiw/codemirror-theme-okaidia": "^4.23.10",
133
- "@uiw/codemirror-theme-xcode": "^4.23.10",
134
- "@uiw/codemirror-themes": "^4.23.10",
135
- "@vizhub/viz-types": "^0.0.2",
127
+ "@uiw/codemirror-theme-abcdef": "^4.23.12",
128
+ "@uiw/codemirror-theme-dracula": "^4.23.12",
129
+ "@uiw/codemirror-theme-eclipse": "^4.23.12",
130
+ "@uiw/codemirror-theme-github": "^4.23.12",
131
+ "@uiw/codemirror-theme-material": "^4.23.12",
132
+ "@uiw/codemirror-theme-nord": "^4.23.12",
133
+ "@uiw/codemirror-theme-okaidia": "^4.23.12",
134
+ "@uiw/codemirror-theme-xcode": "^4.23.12",
135
+ "@uiw/codemirror-themes": "^4.23.12",
136
+ "@valtown/codemirror-ts": "^2.3.1",
137
+ "@vizhub/viz-types": "^0.1.0",
136
138
  "body-parser": "^2.2.0",
137
139
  "codemirror": "^6.0.1",
138
140
  "codemirror-copilot": "^0.0.7",
139
141
  "codemirror-ot": "^4.4.0",
140
142
  "color-hash": "^2.0.2",
143
+ "comlink": "^4.4.2",
141
144
  "d3-array": "^3.2.4",
142
145
  "diff-match-patch": "^1.0.5",
143
- "dotenv": "^16.4.7",
146
+ "dotenv": "^16.5.0",
147
+ "eslint": "^9.28.0",
148
+ "eslint-linter-browserify": "^9.28.0",
144
149
  "express": "^5.1.0",
145
- "ignore": "^7.0.3",
150
+ "globals": "^16.2.0",
151
+ "ignore": "^7.0.5",
146
152
  "json0-ot-diff": "^1.1.2",
147
- "livekit-server-sdk": "^2.11.0",
153
+ "livekit-server-sdk": "^2.13.0",
148
154
  "ngrok": "^5.0.0-beta.2",
149
- "npm": "^11.2.0",
150
- "open": "^10.1.0",
151
- "prettier-plugin-svelte": "^3.3.3",
155
+ "npm": "^11.4.1",
156
+ "open": "^10.1.2",
157
+ "prettier-plugin-svelte": "^3.4.0",
152
158
  "react": "^18",
153
- "react-bootstrap": "^2.10.9",
159
+ "react-bootstrap": "^2.10.10",
154
160
  "react-dom": "^18",
155
- "react-router-dom": "^7.4.1",
156
- "sharedb": "^5.2.1",
157
- "sharedb-client-browser": "^5.1.1",
161
+ "react-router-dom": "^7.6.2",
162
+ "sharedb": "^5.2.2",
163
+ "sharedb-client-browser": "^5.2.2",
158
164
  "uuid": "^11.1.0",
159
165
  "vizhub-ui": "^3.23.0",
160
- "ws": "^8.18.1"
166
+ "ws": "^8.18.2"
161
167
  },
162
168
  "devDependencies": {
163
169
  "@types/react": "^18",
164
170
  "@types/react-dom": "^18",
165
- "@vitejs/plugin-react": "^4.3.4",
171
+ "@vitejs/plugin-react": "^4.5.1",
166
172
  "concurrently": "^9.1.2",
167
173
  "cross-env": "^7.0.3",
168
- "npm-check-updates": "^17.1.16",
174
+ "npm-check-updates": "^18.0.1",
169
175
  "prettier": "^3.5.3",
170
- "sass": "^1.86.2",
171
- "typescript": "^5.8.2",
172
- "vite": "^6.2.5",
173
- "vitest": "^3.1.1"
176
+ "sass": "^1.89.1",
177
+ "typescript": "^5.8.3",
178
+ "vite": "^6.3.5",
179
+ "vitest": "^3.2.2"
174
180
  },
175
181
  "optionalDependencies": {
176
- "@rollup/rollup-darwin-arm64": "^4.39.0",
177
- "@rollup/rollup-win32-x64-msvc": "^4.39.0"
182
+ "@rollup/rollup-darwin-arm64": "^4.42.0",
183
+ "@rollup/rollup-win32-x64-msvc": "^4.42.0"
178
184
  }
179
185
  }
@@ -70,30 +70,6 @@ export const startAIAssist = async ({
70
70
  .join('\n\n');
71
71
  }
72
72
 
73
- if (debug) {
74
- // console.log('[startAIAssist] sending HTTP request:');
75
- // console.log(
76
- // '[startAIAssist] aiAssistEndpoint:',
77
- // aiAssistEndpoint,
78
- // );
79
- // console.log(
80
- // '[startAIAssist] aiAssistOptions:',
81
- // aiAssistOptions,
82
- // );
83
- console.log(
84
- '[startAIAssist] inputText:\n`' + inputText + '`',
85
- );
86
- // console.log('[startAIAssist] fileId:', fileId);
87
- // console.log(
88
- // '[startAIAssist] insertionCursor:',
89
- // insertionCursor,
90
- // );
91
- // console.log(
92
- // '[startAIAssist] aiStreamId:',
93
- // aiStreamId,
94
- // );
95
- }
96
-
97
73
  const response = await fetch(aiAssistEndpoint, {
98
74
  method: 'POST',
99
75
  headers: {
@@ -19,20 +19,17 @@ import { VZLeft } from '../VZLeft';
19
19
  import { VZMiddle } from '../VZMiddle';
20
20
  import { VZResizer } from '../VZResizer';
21
21
  import { VZRight } from '../VZRight';
22
- import TypeScriptWorker from '../useTypeScript/worker?worker';
23
22
  import {
24
23
  useInitialUsername,
25
24
  usePersistUsername,
26
25
  } from '../usernameLocalStorage';
27
26
  import './style.scss';
28
27
  import { useShareDB } from './useShareDB';
28
+ import { useESLint } from '../useESLint';
29
29
 
30
30
  // Instantiate the Prettier worker.
31
31
  const prettierWorker = new PrettierWorker();
32
32
 
33
- // Instantiate the TypeScript worker.
34
- const typeScriptWorker = new TypeScriptWorker();
35
-
36
33
  // Register our custom JSON1 OT type that supports presence.
37
34
  // See https://github.com/vizhub-core/json1-presence
38
35
  ShareDBClient.types.register(json1Presence.type);
@@ -79,6 +76,7 @@ function App() {
79
76
  useState(v4()); //default room name will be an arbitrary uuid
80
77
  const [liveKitConnection, setLiveKitConnection] =
81
78
  useState(false);
79
+ const { esLintSource } = useESLint();
82
80
  // Get the initial username from localStorage.
83
81
  const initialUsername: Username = useInitialUsername();
84
82
 
@@ -93,6 +91,14 @@ function App() {
93
91
  // so that multiple browser windows are not required.
94
92
  const enableRightPanel = true;
95
93
 
94
+ // TODO: enable this when the AI copilot is ready
95
+ // Currently it's glitchy and frustrating to use.
96
+ // It appears to be bugs in the upstream CodeMirror copilot code.
97
+ // Ghost text appears after just moving the cursor, not typing.
98
+ // It's hard to figure out how to dismiss it.
99
+ // Clicking on it accepts it but should not.
100
+ const enableCopilot = false;
101
+
96
102
  return (
97
103
  <SplitPaneResizeProvider>
98
104
  <VZCodeProvider
@@ -102,7 +108,6 @@ function App() {
102
108
  localPresence={localPresence}
103
109
  docPresence={docPresence}
104
110
  prettierWorker={prettierWorker}
105
- typeScriptWorker={typeScriptWorker}
106
111
  initialUsername={initialUsername}
107
112
  connected={connected}
108
113
  pending={pending}
@@ -123,8 +128,10 @@ function App() {
123
128
  <div className="app">
124
129
  <VZLeft />
125
130
  <VZMiddle
126
- allowGlobals={true}
127
- aiCopilotEndpoint={'/ai-copilot'}
131
+ aiCopilotEndpoint={
132
+ enableCopilot ? '/ai-copilot' : null
133
+ }
134
+ esLintSource={esLintSource}
128
135
  />
129
136
  {enableRightPanel ? <VZRight /> : null}
130
137
  <VZResizer side="left" />
@@ -2,6 +2,7 @@ import { EditorView } from 'codemirror';
2
2
  import {
3
3
  Compartment,
4
4
  EditorState,
5
+ StateField,
5
6
  } from '@codemirror/state';
6
7
  import { javascript } from '@codemirror/lang-javascript';
7
8
  import { svelte } from '@replit/codemirror-lang-svelte';
@@ -15,7 +16,11 @@ import { autocompletion } from '@codemirror/autocomplete';
15
16
  import { indentationMarkers } from '@replit/codemirror-indentation-markers';
16
17
  // import { showMinimap } from '@replit/codemirror-minimap';
17
18
  import { vscodeKeymap } from '@replit/codemirror-vscode-keymap';
18
- import { Diagnostic, linter } from '@codemirror/lint';
19
+ import {
20
+ Diagnostic,
21
+ linter,
22
+ lintGutter,
23
+ } from '@codemirror/lint';
19
24
 
20
25
  import { json1Presence, textUnicode } from '../../ot';
21
26
  import {
@@ -38,30 +43,57 @@ import {
38
43
  EditorCacheValue,
39
44
  } from '../useEditorCache';
40
45
  import { ThemeLabel, themeOptionsByLabel } from '../themes';
41
- import { typeScriptCompletions } from './typeScriptCompletions';
42
- import { typeScriptLinter } from './typeScriptLinter';
43
46
  import { keymap } from '@codemirror/view';
44
47
  import { basicSetup } from './basicSetup';
45
48
  import { InteractRule } from '@replit/codemirror-interact';
46
- import rainbowBrackets from '../CodeEditor/rainbowBrackets';
49
+ import rainbowBrackets from './rainbowBrackets';
47
50
  import { cssLanguage } from '@codemirror/lang-css';
48
51
  import { javascriptLanguage } from '@codemirror/lang-javascript';
49
52
  import { copilot } from './Copilot';
50
-
51
- // Feature flag to enable TypeScript completions & TypeScript Linter.
52
- // This is disabled by default because it's buggy and glitchy.
53
- // It's overly aggressive and shows warnings where it should not.
54
- // Path forward:
55
- // * Remove this entire TypeScript linter implementation
56
- // * Use val-town/codemirror-ts: https://github.com/vizhub-core/vzcode/issues/844
57
- const enableTypeScriptLinter = false;
53
+ import { type WorkerShape } from '@valtown/codemirror-ts/src/worker';
54
+ import * as Comlink from 'comlink';
55
+ import {
56
+ tsAutocompleteWorker,
57
+ tsFacetWorker,
58
+ tsHoverWorker,
59
+ tsLinterWorker,
60
+ tsSyncWorker,
61
+ } from '@valtown/codemirror-ts';
58
62
 
59
63
  const DEBUG = false;
60
64
 
65
+ // Define a StateField to store the file name.
66
+ // This should be defined at the module level if it's to be imported by other modules.
67
+ export const fileNameStateField = StateField.define<string>(
68
+ {
69
+ create: () => '', // Default initial value
70
+ update: (value, tr) => value, // Typically set once at creation for a given editor instance
71
+ },
72
+ );
73
+
61
74
  // Enables TypeScript +JSX support in CodeMirror.
62
75
  const tsx = () =>
63
76
  javascript({ jsx: true, typescript: true });
64
77
 
78
+ // Lazy initialization of worker
79
+ let worker: Comlink.Remote<WorkerShape> | null = null;
80
+ const initializeWorker = async () => {
81
+ if (worker === null) {
82
+ const innerWorker = new Worker(
83
+ new URL(
84
+ './typescriptExtension/worker.ts',
85
+ import.meta.url,
86
+ ),
87
+ {
88
+ type: 'module',
89
+ },
90
+ );
91
+ worker = Comlink.wrap<WorkerShape>(innerWorker);
92
+ await worker.initialize();
93
+ }
94
+ return worker;
95
+ };
96
+
65
97
  const htmlConfig = {
66
98
  matchClosingTags: true,
67
99
  selfClosingTags: false,
@@ -120,7 +152,7 @@ interface ExtendedEditorCacheValue
120
152
 
121
153
  // Gets or creates an `editorCache` entry for the given file id.
122
154
  // Looks in `editorCache` first, and if not found, creates a new editor.
123
- export const getOrCreateEditor = ({
155
+ export const getOrCreateEditor = async ({
124
156
  paneId = 'root',
125
157
  fileId,
126
158
  shareDBDoc,
@@ -132,12 +164,11 @@ export const getOrCreateEditor = ({
132
164
  onInteract,
133
165
  editorCache,
134
166
  usernameRef,
135
- typeScriptWorker,
136
167
  customInteractRules,
137
- allowGlobals,
138
168
  enableAutoFollowRef,
139
169
  openTab,
140
170
  aiCopilotEndpoint,
171
+ esLintSource,
141
172
  rainbowBracketsEnabled = true,
142
173
  }: {
143
174
  // TODO pass this in from the outside
@@ -167,17 +198,18 @@ export const getOrCreateEditor = ({
167
198
  editorCache: EditorCache;
168
199
  usernameRef: React.MutableRefObject<Username>;
169
200
  aiAssistEndpoint?: string;
170
- typeScriptWorker: Worker;
171
201
  customInteractRules?: Array<InteractRule>;
172
- allowGlobals: boolean;
173
202
 
174
203
  // Ref to a boolean that determines whether to
175
204
  // enable auto-following the cursors of remote users.
176
205
  enableAutoFollowRef: React.MutableRefObject<boolean>;
177
206
  openTab: (tabState: TabState) => void;
178
207
  aiCopilotEndpoint?: string;
208
+ esLintSource: (
209
+ view: EditorView,
210
+ ) => Promise<readonly Diagnostic[]>;
179
211
  rainbowBracketsEnabled?: boolean; // New parameter type
180
- }): ExtendedEditorCacheValue => {
212
+ }): Promise<ExtendedEditorCacheValue> => {
181
213
  // Cache hit
182
214
 
183
215
  const cacheKey = editorCacheKey(fileId, paneId);
@@ -208,6 +240,9 @@ export const getOrCreateEditor = ({
208
240
  // const extensions = [autocompletion(), html(htmlConfig)]
209
241
  const extensions = [];
210
242
 
243
+ // Initialize the fileNameStateField with the actual file name
244
+ extensions.push(fileNameStateField.init(() => name));
245
+
211
246
  // This plugin implements multiplayer editing,
212
247
  // real-time synchronozation of changes across clients.
213
248
  // Does not deal with showing others' cursors.
@@ -258,6 +293,16 @@ export const getOrCreateEditor = ({
258
293
  // https://github.com/vizhub-core/vzcode/issues/134
259
294
  extensions.push(basicSetup);
260
295
 
296
+ if (esLintSource) {
297
+ extensions.push(lintGutter()); // Show lint icons in the gutter
298
+ extensions.push(
299
+ linter(esLintSource, {
300
+ // You can configure linter options here, e.g., delay
301
+ delay: 750,
302
+ }),
303
+ );
304
+ }
305
+
261
306
  // This supports dynamic changing of the theme.
262
307
  extensions.push(
263
308
  themeCompartment.of(themeOptionsByLabel[theme].value),
@@ -333,44 +378,6 @@ export const getOrCreateEditor = ({
333
378
  // }),
334
379
  // );
335
380
 
336
- // Add the extension that provides TypeScript completions.
337
- // only add this if it's TS-compatible (.js, .jsx, .ts, .tsx)
338
- const isTypeScript =
339
- fileExtension === 'js' ||
340
- fileExtension === 'jsx' ||
341
- fileExtension === 'ts' ||
342
- fileExtension === 'tsx';
343
-
344
- extensions.push(
345
- autocompletion(
346
- isTypeScript
347
- ? {
348
- override: [
349
- typeScriptCompletions({
350
- typeScriptWorker,
351
- fileName: name,
352
- }),
353
- ],
354
- }
355
- : undefined,
356
- ),
357
- );
358
-
359
- if (shareDBDoc && enableTypeScriptLinter) {
360
- extensions.push(
361
- linter(
362
- typeScriptLinter({
363
- typeScriptWorker,
364
- fileName: name,
365
- shareDBDoc,
366
- fileId,
367
- allowGlobals,
368
- }) as unknown as () => Diagnostic[],
369
- //Needs the unknown because we are returning a Promise<Diagnostic>
370
- ),
371
- );
372
- }
373
-
374
381
  // Add the extension that provides indentation markers.
375
382
  extensions.push(
376
383
  indentationMarkers({
@@ -384,6 +391,23 @@ export const getOrCreateEditor = ({
384
391
  }),
385
392
  );
386
393
 
394
+ if (name.endsWith('ts') || name.endsWith('tsx')) {
395
+ // Initialize worker if needed
396
+ const tsWorker = await initializeWorker();
397
+
398
+ extensions.push(
399
+ ...[
400
+ tsFacetWorker.of({ worker: tsWorker, path: name }),
401
+ tsSyncWorker(),
402
+ tsLinterWorker(),
403
+ autocompletion({
404
+ override: [tsAutocompleteWorker()],
405
+ }),
406
+ tsHoverWorker(),
407
+ ],
408
+ );
409
+ }
410
+
387
411
  // Show the minimap
388
412
  // See https://github.com/replit/codemirror-minimap#usage
389
413
  // This extension has poor performance, so it's disabled for now.
@@ -2,15 +2,17 @@ import {
2
2
  useRef,
3
3
  useLayoutEffect,
4
4
  useCallback,
5
- useMemo,
6
5
  useContext,
7
6
  useEffect,
7
+ useState,
8
8
  } from 'react';
9
9
  import { Username } from '../../types';
10
10
  import { EditorCacheValue } from '../useEditorCache';
11
11
  import { getOrCreateEditor } from './getOrCreateEditor';
12
12
  import { VZCodeContext } from '../VZCodeContext';
13
13
  import { InteractRule } from '@replit/codemirror-interact';
14
+ import { EditorView } from 'codemirror'; // Import EditorView
15
+ import { Diagnostic } from '@codemirror/lint'; // Import Diagnostic
14
16
  import './style.scss';
15
17
 
16
18
  // The path in the ShareDB document where the files live.
@@ -18,12 +20,14 @@ const filesPath = ['files'];
18
20
 
19
21
  export const CodeEditor = ({
20
22
  customInteractRules,
21
- allowGlobals,
22
23
  aiCopilotEndpoint,
24
+ esLintSource,
23
25
  }: {
24
26
  customInteractRules?: Array<InteractRule>;
25
- allowGlobals: boolean;
26
27
  aiCopilotEndpoint?: string;
28
+ esLintSource: (
29
+ view: EditorView,
30
+ ) => Promise<readonly Diagnostic[]>;
27
31
  }) => {
28
32
  const {
29
33
  activePane,
@@ -36,7 +40,6 @@ export const CodeEditor = ({
36
40
  editorCache,
37
41
  editorWantsFocus,
38
42
  editorNoLongerWantsFocus,
39
- typeScriptWorker,
40
43
  theme,
41
44
  codeEditorRef,
42
45
  enableAutoFollow,
@@ -81,11 +84,17 @@ export const CodeEditor = ({
81
84
  enableAutoFollowRef.current = enableAutoFollow;
82
85
  }, [enableAutoFollow]);
83
86
 
87
+ // State to hold the editor cache value
88
+ const [editorCacheValue, setEditorCacheValue] =
89
+ useState<EditorCacheValue | null>(null);
90
+
84
91
  // Get the editor corresponding to the active file.
85
92
  // Looks in `editorCache` first, and if not found, creates a new editor.
86
- const editorCacheValue: EditorCacheValue = useMemo(
87
- () =>
88
- getOrCreateEditor({
93
+ useEffect(() => {
94
+ let isMounted = true;
95
+
96
+ const initEditor = async () => {
97
+ const editor = await getOrCreateEditor({
89
98
  fileId: activePane.activeFileId,
90
99
  shareDBDoc,
91
100
  content,
@@ -96,27 +105,36 @@ export const CodeEditor = ({
96
105
  onInteract,
97
106
  editorCache,
98
107
  usernameRef,
99
- typeScriptWorker,
100
108
  customInteractRules,
101
- allowGlobals,
102
109
  enableAutoFollowRef,
103
110
  openTab,
104
111
  aiCopilotEndpoint,
105
- }),
106
- [
107
- activePane,
108
- shareDBDoc,
109
- filesPath,
110
- localPresence,
111
- docPresence,
112
- theme,
113
- onInteract,
114
- editorCache,
115
- usernameRef,
116
- typeScriptWorker,
117
- aiCopilotEndpoint,
118
- ],
119
- );
112
+ esLintSource,
113
+ });
114
+
115
+ if (isMounted) {
116
+ setEditorCacheValue(editor);
117
+ }
118
+ };
119
+
120
+ initEditor();
121
+
122
+ return () => {
123
+ isMounted = false;
124
+ };
125
+ }, [
126
+ activePane,
127
+ shareDBDoc,
128
+ filesPath,
129
+ localPresence,
130
+ docPresence,
131
+ theme,
132
+ onInteract,
133
+ editorCache,
134
+ usernameRef,
135
+ aiCopilotEndpoint,
136
+ esLintSource,
137
+ ]);
120
138
 
121
139
  // Every time the active file switches from one file to another,
122
140
  // the editor corresponding to the old file is removed from the DOM,
@@ -125,6 +143,7 @@ export const CodeEditor = ({
125
143
  // Guard against cases where page is still loading.
126
144
  if (!codeEditorRef.current) return;
127
145
  if (!content) return;
146
+ if (!editorCacheValue) return; // Guard against null editorCacheValue
128
147
  // if (openSplitPane) return;
129
148
 
130
149
  // Add the editor and apply the prior scroll position to the DOM.
@@ -138,17 +157,24 @@ export const CodeEditor = ({
138
157
  return () => {
139
158
  // Remove the old editor from the DOM and store the current scroll position.
140
159
  // This happens every time `activeFileId` changes.
141
- editorCacheValue.scrollPosition =
142
- editorCacheValue.editor.scrollDOM.scrollTop;
143
- codeEditorRef.current.removeChild(
144
- editorCacheValue.editor.dom,
145
- );
160
+ if (
161
+ editorCacheValue &&
162
+ codeEditorRef.current?.contains(
163
+ editorCacheValue.editor.dom,
164
+ )
165
+ ) {
166
+ editorCacheValue.scrollPosition =
167
+ editorCacheValue.editor.scrollDOM.scrollTop;
168
+ codeEditorRef.current.removeChild(
169
+ editorCacheValue.editor.dom,
170
+ );
171
+ }
146
172
  };
147
173
  }, [shareDBDoc, editorCacheValue]);
148
174
 
149
175
  // Focus the editor
150
176
  useEffect(() => {
151
- if (editorWantsFocus) {
177
+ if (editorWantsFocus && editorCacheValue) {
152
178
  editorCacheValue.editor.focus();
153
179
  editorNoLongerWantsFocus();
154
180
  }
@@ -0,0 +1,32 @@
1
+ import {
2
+ createDefaultMapFromCDN,
3
+ createSystem,
4
+ createVirtualTypeScriptEnvironment,
5
+ } from '@typescript/vfs';
6
+ import ts from 'typescript';
7
+ import * as Comlink from 'comlink';
8
+
9
+ // When importing this module, typescript says that it "cannot find the module or its corresponding type declarations"
10
+ // However, when this file is used as a web worker, no error occurs.
11
+ // As a result, this error is suppressed to avoid future confusion.
12
+ // @ts-expect-error
13
+ import { createWorker } from '@valtown/codemirror-ts/worker';
14
+
15
+ Comlink.expose(
16
+ createWorker(async function () {
17
+ const fsMap = await createDefaultMapFromCDN(
18
+ { target: ts.ScriptTarget.ES2022 },
19
+ '3.7.3',
20
+ false,
21
+ ts,
22
+ );
23
+ const system = createSystem(fsMap);
24
+ const compilerOpts = {};
25
+ return createVirtualTypeScriptEnvironment(
26
+ system,
27
+ [],
28
+ ts,
29
+ compilerOpts,
30
+ );
31
+ }),
32
+ );
@@ -48,8 +48,6 @@ export const RunCodeWidget = ({
48
48
  }, [runCodeRef, runPrettierRef]);
49
49
 
50
50
  const handleSplitEditor = useCallback(() => {
51
- console.log('Split Editor');
52
- console.log(splitCurrentPane);
53
51
  splitCurrentPane();
54
52
  }, [splitCurrentPane]);
55
53