vzcode 1.31.0 → 1.33.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/README.md +0 -1
- package/dist/assets/index-DPIrco_g.js +235 -0
- package/dist/assets/index-DnunmWjy.css +1 -0
- package/dist/assets/worker-BrwN8AXx.js +330 -0
- package/dist/assets/worker-CkHcCP1n.js +136 -0
- package/dist/assets/worker-DkYJN5WQ.js +453 -0
- package/dist/index.html +2 -2
- package/package.json +48 -41
- package/src/client/App/index.tsx +3 -0
- package/src/client/CodeEditor/getOrCreateEditor.ts +98 -9
- package/src/client/CodeEditor/index.tsx +56 -24
- package/src/client/CodeEditor/typescriptExtension/worker.ts +32 -0
- package/src/client/Icons/MicSVG.tsx +1 -1
- package/src/client/VZCodeContext.tsx +18 -2
- package/src/client/VZMiddle.tsx +11 -0
- package/src/client/VZSidebar/AIChat.tsx +142 -0
- package/src/client/VZSidebar/Search.tsx +11 -0
- package/src/client/VZSidebar/index.tsx +53 -8
- package/src/client/VZSidebar/styles.scss +178 -1
- package/src/client/featureFlags.ts +2 -0
- package/src/client/useActions.ts +20 -0
- package/src/client/useESLint/index.ts +72 -0
- package/src/client/useESLint/worker.ts +113 -0
- package/src/client/useEditorCache.ts +1 -0
- package/src/client/useFileCRUD.ts +47 -0
- package/src/client/useOpenDirectories.ts +43 -12
- package/src/client/utils/fileExtension.ts +10 -0
- package/src/client/vzReducer/aiChatReducer.ts +31 -0
- package/src/client/vzReducer/createInitialState.ts +2 -0
- package/src/client/vzReducer/index.ts +20 -0
- package/src/server/featureFlags.js +2 -0
- package/dist/assets/index-D01yzCWj.js +0 -234
- package/dist/assets/index-DTtcN2MP.css +0 -1
- package/dist/assets/worker-CaP6zYz7.js +0 -335
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-
|
|
24
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
23
|
+
<script type="module" crossorigin src="/assets/index-DPIrco_g.js"></script>
|
|
24
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DnunmWjy.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.
|
|
3
|
+
"version": "1.33.0",
|
|
4
4
|
"description": "Multiplayer code editor system",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -107,72 +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.
|
|
111
|
-
"@codemirror/lang-json": "^6.0.
|
|
112
|
-
"@codemirror/lang-markdown": "^6.3.
|
|
110
|
+
"@codemirror/lang-javascript": "^6.2.4",
|
|
111
|
+
"@codemirror/lang-json": "^6.0.2",
|
|
112
|
+
"@codemirror/lang-markdown": "^6.3.3",
|
|
113
|
+
"@codemirror/lint": "^6.8.5",
|
|
113
114
|
"@codemirror/state": "^6.5.2",
|
|
114
|
-
"@codemirror/theme-one-dark": "^6.1.
|
|
115
|
-
"@codemirror/view": "^6.
|
|
116
|
-
"@langchain/core": "^0.3.
|
|
117
|
-
"@langchain/openai": "^0.
|
|
115
|
+
"@codemirror/theme-one-dark": "^6.1.3",
|
|
116
|
+
"@codemirror/view": "^6.38.1",
|
|
117
|
+
"@langchain/core": "^0.3.65",
|
|
118
|
+
"@langchain/openai": "^0.6.2",
|
|
118
119
|
"@lezer/highlight": "^1.2.1",
|
|
119
|
-
"@livekit/components-react": "^2.9.
|
|
120
|
+
"@livekit/components-react": "^2.9.14",
|
|
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
|
-
"@uiw/codemirror-theme-
|
|
127
|
-
"@uiw/codemirror-theme-
|
|
128
|
-
"@uiw/codemirror-theme-
|
|
129
|
-
"@uiw/codemirror-theme-
|
|
130
|
-
"@uiw/codemirror-theme-
|
|
131
|
-
"@uiw/codemirror-theme-
|
|
132
|
-
"@uiw/codemirror-theme-
|
|
133
|
-
"@uiw/codemirror-
|
|
134
|
-
"@
|
|
126
|
+
"@typescript/vfs": "^1.6.1",
|
|
127
|
+
"@uiw/codemirror-theme-abcdef": "^4.24.1",
|
|
128
|
+
"@uiw/codemirror-theme-dracula": "^4.24.1",
|
|
129
|
+
"@uiw/codemirror-theme-eclipse": "^4.24.1",
|
|
130
|
+
"@uiw/codemirror-theme-github": "^4.24.1",
|
|
131
|
+
"@uiw/codemirror-theme-material": "^4.24.1",
|
|
132
|
+
"@uiw/codemirror-theme-nord": "^4.24.1",
|
|
133
|
+
"@uiw/codemirror-theme-okaidia": "^4.24.1",
|
|
134
|
+
"@uiw/codemirror-theme-xcode": "^4.24.1",
|
|
135
|
+
"@uiw/codemirror-themes": "^4.24.1",
|
|
136
|
+
"@valtown/codemirror-ts": "^2.3.1",
|
|
137
|
+
"@vizhub/viz-types": "^0.2.0",
|
|
135
138
|
"body-parser": "^2.2.0",
|
|
136
|
-
"codemirror": "^6.0.
|
|
139
|
+
"codemirror": "^6.0.2",
|
|
137
140
|
"codemirror-copilot": "^0.0.7",
|
|
138
141
|
"codemirror-ot": "^4.4.0",
|
|
139
142
|
"color-hash": "^2.0.2",
|
|
143
|
+
"comlink": "^4.4.2",
|
|
140
144
|
"d3-array": "^3.2.4",
|
|
141
145
|
"diff-match-patch": "^1.0.5",
|
|
142
|
-
"dotenv": "^
|
|
146
|
+
"dotenv": "^17.2.0",
|
|
147
|
+
"eslint": "^9.31.0",
|
|
148
|
+
"eslint-linter-browserify": "^9.31.0",
|
|
143
149
|
"express": "^5.1.0",
|
|
144
|
-
"
|
|
150
|
+
"globals": "^16.3.0",
|
|
151
|
+
"ignore": "^7.0.5",
|
|
145
152
|
"json0-ot-diff": "^1.1.2",
|
|
146
|
-
"livekit-server-sdk": "^2.
|
|
153
|
+
"livekit-server-sdk": "^2.13.1",
|
|
147
154
|
"ngrok": "^5.0.0-beta.2",
|
|
148
|
-
"npm": "^11.2
|
|
149
|
-
"open": "^10.
|
|
150
|
-
"prettier-plugin-svelte": "^3.
|
|
155
|
+
"npm": "^11.4.2",
|
|
156
|
+
"open": "^10.2.0",
|
|
157
|
+
"prettier-plugin-svelte": "^3.4.0",
|
|
151
158
|
"react": "^18",
|
|
152
|
-
"react-bootstrap": "^2.10.
|
|
159
|
+
"react-bootstrap": "^2.10.10",
|
|
153
160
|
"react-dom": "^18",
|
|
154
|
-
"react-router-dom": "^7.
|
|
155
|
-
"sharedb": "^5.2.
|
|
156
|
-
"sharedb-client-browser": "^5.
|
|
161
|
+
"react-router-dom": "^7.7.0",
|
|
162
|
+
"sharedb": "^5.2.2",
|
|
163
|
+
"sharedb-client-browser": "^5.2.2",
|
|
157
164
|
"uuid": "^11.1.0",
|
|
158
|
-
"vizhub-ui": "^3.
|
|
159
|
-
"ws": "^8.18.
|
|
165
|
+
"vizhub-ui": "^3.24.0",
|
|
166
|
+
"ws": "^8.18.3"
|
|
160
167
|
},
|
|
161
168
|
"devDependencies": {
|
|
162
169
|
"@types/react": "^18",
|
|
163
170
|
"@types/react-dom": "^18",
|
|
164
|
-
"@vitejs/plugin-react": "^4.
|
|
165
|
-
"concurrently": "^9.
|
|
171
|
+
"@vitejs/plugin-react": "^4.7.0",
|
|
172
|
+
"concurrently": "^9.2.0",
|
|
166
173
|
"cross-env": "^7.0.3",
|
|
167
|
-
"npm-check-updates": "^
|
|
168
|
-
"prettier": "^3.
|
|
169
|
-
"sass": "^1.
|
|
174
|
+
"npm-check-updates": "^18.0.1",
|
|
175
|
+
"prettier": "^3.6.2",
|
|
176
|
+
"sass": "^1.89.2",
|
|
170
177
|
"typescript": "^5.8.3",
|
|
171
|
-
"vite": "^
|
|
172
|
-
"vitest": "^3.
|
|
178
|
+
"vite": "^7.0.5",
|
|
179
|
+
"vitest": "^3.2.4"
|
|
173
180
|
},
|
|
174
181
|
"optionalDependencies": {
|
|
175
|
-
"@rollup/rollup-darwin-arm64": "^4.
|
|
176
|
-
"@rollup/rollup-win32-x64-msvc": "^4.
|
|
182
|
+
"@rollup/rollup-darwin-arm64": "^4.45.1",
|
|
183
|
+
"@rollup/rollup-win32-x64-msvc": "^4.45.1"
|
|
177
184
|
}
|
|
178
185
|
}
|
package/src/client/App/index.tsx
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
} from '../usernameLocalStorage';
|
|
26
26
|
import './style.scss';
|
|
27
27
|
import { useShareDB } from './useShareDB';
|
|
28
|
+
import { useESLint } from '../useESLint';
|
|
28
29
|
|
|
29
30
|
// Instantiate the Prettier worker.
|
|
30
31
|
const prettierWorker = new PrettierWorker();
|
|
@@ -75,6 +76,7 @@ function App() {
|
|
|
75
76
|
useState(v4()); //default room name will be an arbitrary uuid
|
|
76
77
|
const [liveKitConnection, setLiveKitConnection] =
|
|
77
78
|
useState(false);
|
|
79
|
+
const { esLintSource } = useESLint();
|
|
78
80
|
// Get the initial username from localStorage.
|
|
79
81
|
const initialUsername: Username = useInitialUsername();
|
|
80
82
|
|
|
@@ -129,6 +131,7 @@ function App() {
|
|
|
129
131
|
aiCopilotEndpoint={
|
|
130
132
|
enableCopilot ? '/ai-copilot' : null
|
|
131
133
|
}
|
|
134
|
+
esLintSource={esLintSource}
|
|
132
135
|
/>
|
|
133
136
|
{enableRightPanel ? <VZRight /> : null}
|
|
134
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 {
|
|
19
|
+
import {
|
|
20
|
+
Diagnostic,
|
|
21
|
+
linter,
|
|
22
|
+
lintGutter,
|
|
23
|
+
} from '@codemirror/lint';
|
|
19
24
|
|
|
20
25
|
import { json1Presence, textUnicode } from '../../ot';
|
|
21
26
|
import {
|
|
@@ -41,17 +46,55 @@ import { ThemeLabel, themeOptionsByLabel } from '../themes';
|
|
|
41
46
|
import { keymap } from '@codemirror/view';
|
|
42
47
|
import { basicSetup } from './basicSetup';
|
|
43
48
|
import { InteractRule } from '@replit/codemirror-interact';
|
|
44
|
-
import rainbowBrackets from '
|
|
49
|
+
import rainbowBrackets from './rainbowBrackets';
|
|
45
50
|
import { cssLanguage } from '@codemirror/lang-css';
|
|
46
51
|
import { javascriptLanguage } from '@codemirror/lang-javascript';
|
|
47
52
|
import { copilot } from './Copilot';
|
|
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';
|
|
62
|
+
import { getFileExtension } from '../utils/fileExtension';
|
|
48
63
|
|
|
49
64
|
const DEBUG = false;
|
|
50
65
|
|
|
66
|
+
// Define a StateField to store the file name.
|
|
67
|
+
// This should be defined at the module level if it's to be imported by other modules.
|
|
68
|
+
export const fileNameStateField = StateField.define<string>(
|
|
69
|
+
{
|
|
70
|
+
create: () => '', // Default initial value
|
|
71
|
+
update: (value, tr) => value, // Typically set once at creation for a given editor instance
|
|
72
|
+
},
|
|
73
|
+
);
|
|
74
|
+
|
|
51
75
|
// Enables TypeScript +JSX support in CodeMirror.
|
|
52
76
|
const tsx = () =>
|
|
53
77
|
javascript({ jsx: true, typescript: true });
|
|
54
78
|
|
|
79
|
+
// Lazy initialization of worker
|
|
80
|
+
let worker: Comlink.Remote<WorkerShape> | null = null;
|
|
81
|
+
const initializeWorker = async () => {
|
|
82
|
+
if (worker === null) {
|
|
83
|
+
const innerWorker = new Worker(
|
|
84
|
+
new URL(
|
|
85
|
+
'./typescriptExtension/worker.ts',
|
|
86
|
+
import.meta.url,
|
|
87
|
+
),
|
|
88
|
+
{
|
|
89
|
+
type: 'module',
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
worker = Comlink.wrap<WorkerShape>(innerWorker);
|
|
93
|
+
await worker.initialize();
|
|
94
|
+
}
|
|
95
|
+
return worker;
|
|
96
|
+
};
|
|
97
|
+
|
|
55
98
|
const htmlConfig = {
|
|
56
99
|
matchClosingTags: true,
|
|
57
100
|
selfClosingTags: false,
|
|
@@ -89,6 +132,14 @@ const languageExtensions = {
|
|
|
89
132
|
svelte,
|
|
90
133
|
};
|
|
91
134
|
|
|
135
|
+
// Exported utility function to get language extension for a file extension
|
|
136
|
+
export const getLanguageExtension = (
|
|
137
|
+
fileExtension: string,
|
|
138
|
+
) => {
|
|
139
|
+
const languageFunc = languageExtensions[fileExtension];
|
|
140
|
+
return languageFunc ? languageFunc() : undefined;
|
|
141
|
+
};
|
|
142
|
+
|
|
92
143
|
// Gets a value at a path in an object.
|
|
93
144
|
// e.g. getAtPath({a: {b: 1}}, ['a', 'b']) === 1
|
|
94
145
|
const getAtPath = (obj, path) => {
|
|
@@ -104,13 +155,14 @@ const getAtPath = (obj, path) => {
|
|
|
104
155
|
interface ExtendedEditorCacheValue
|
|
105
156
|
extends EditorCacheValue {
|
|
106
157
|
themeCompartment: Compartment;
|
|
158
|
+
languageCompartment: Compartment;
|
|
107
159
|
rainbowBracketsCompartment: Compartment;
|
|
108
160
|
updateRainbowBrackets: (enabled: boolean) => void;
|
|
109
161
|
}
|
|
110
162
|
|
|
111
163
|
// Gets or creates an `editorCache` entry for the given file id.
|
|
112
164
|
// Looks in `editorCache` first, and if not found, creates a new editor.
|
|
113
|
-
export const getOrCreateEditor = ({
|
|
165
|
+
export const getOrCreateEditor = async ({
|
|
114
166
|
paneId = 'root',
|
|
115
167
|
fileId,
|
|
116
168
|
shareDBDoc,
|
|
@@ -126,6 +178,7 @@ export const getOrCreateEditor = ({
|
|
|
126
178
|
enableAutoFollowRef,
|
|
127
179
|
openTab,
|
|
128
180
|
aiCopilotEndpoint,
|
|
181
|
+
esLintSource,
|
|
129
182
|
rainbowBracketsEnabled = true,
|
|
130
183
|
}: {
|
|
131
184
|
// TODO pass this in from the outside
|
|
@@ -162,8 +215,11 @@ export const getOrCreateEditor = ({
|
|
|
162
215
|
enableAutoFollowRef: React.MutableRefObject<boolean>;
|
|
163
216
|
openTab: (tabState: TabState) => void;
|
|
164
217
|
aiCopilotEndpoint?: string;
|
|
218
|
+
esLintSource: (
|
|
219
|
+
view: EditorView,
|
|
220
|
+
) => Promise<readonly Diagnostic[]>;
|
|
165
221
|
rainbowBracketsEnabled?: boolean; // New parameter type
|
|
166
|
-
}): ExtendedEditorCacheValue => {
|
|
222
|
+
}): Promise<ExtendedEditorCacheValue> => {
|
|
167
223
|
// Cache hit
|
|
168
224
|
|
|
169
225
|
const cacheKey = editorCacheKey(fileId, paneId);
|
|
@@ -181,7 +237,8 @@ export const getOrCreateEditor = ({
|
|
|
181
237
|
const namePath = [...filesPath, fileId, 'name'];
|
|
182
238
|
const text = getAtPath(content, textPath);
|
|
183
239
|
const name = getAtPath(content, namePath);
|
|
184
|
-
|
|
240
|
+
|
|
241
|
+
const fileExtension = getFileExtension(name);
|
|
185
242
|
|
|
186
243
|
// Create a compartment for the theme so that it can be changed dynamically.
|
|
187
244
|
// Inspired by: https://github.com/craftzdog/cm6-themes/blob/main/example/index.ts
|
|
@@ -194,6 +251,9 @@ export const getOrCreateEditor = ({
|
|
|
194
251
|
// const extensions = [autocompletion(), html(htmlConfig)]
|
|
195
252
|
const extensions = [];
|
|
196
253
|
|
|
254
|
+
// Initialize the fileNameStateField with the actual file name
|
|
255
|
+
extensions.push(fileNameStateField.init(() => name));
|
|
256
|
+
|
|
197
257
|
// This plugin implements multiplayer editing,
|
|
198
258
|
// real-time synchronozation of changes across clients.
|
|
199
259
|
// Does not deal with showing others' cursors.
|
|
@@ -244,6 +304,16 @@ export const getOrCreateEditor = ({
|
|
|
244
304
|
// https://github.com/vizhub-core/vzcode/issues/134
|
|
245
305
|
extensions.push(basicSetup);
|
|
246
306
|
|
|
307
|
+
if (esLintSource) {
|
|
308
|
+
extensions.push(lintGutter()); // Show lint icons in the gutter
|
|
309
|
+
extensions.push(
|
|
310
|
+
linter(esLintSource, {
|
|
311
|
+
// You can configure linter options here, e.g., delay
|
|
312
|
+
delay: 750,
|
|
313
|
+
}),
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
247
317
|
// This supports dynamic changing of the theme.
|
|
248
318
|
extensions.push(
|
|
249
319
|
themeCompartment.of(themeOptionsByLabel[theme].value),
|
|
@@ -262,10 +332,6 @@ export const getOrCreateEditor = ({
|
|
|
262
332
|
// using a Compartment.
|
|
263
333
|
const languageCompartment = new Compartment();
|
|
264
334
|
// See https://github.com/vizhub-core/vzcode/issues/55
|
|
265
|
-
const getLanguageExtension = (fileExtension: string) => {
|
|
266
|
-
const languageFunc = languageExtensions[fileExtension];
|
|
267
|
-
return languageFunc ? languageFunc() : undefined;
|
|
268
|
-
};
|
|
269
335
|
|
|
270
336
|
const languageExtension =
|
|
271
337
|
getLanguageExtension(fileExtension);
|
|
@@ -287,6 +353,11 @@ export const getOrCreateEditor = ({
|
|
|
287
353
|
extensions.push(languageCompartment.of([]));
|
|
288
354
|
}
|
|
289
355
|
|
|
356
|
+
// Enable line wrapping for Markdown files
|
|
357
|
+
if (fileExtension === 'md') {
|
|
358
|
+
extensions.push(EditorView.lineWrapping);
|
|
359
|
+
}
|
|
360
|
+
|
|
290
361
|
// Add interactive widgets.
|
|
291
362
|
// Includes the Alt+drag functionality for numbers.
|
|
292
363
|
// Calls `onInteract` when one of those widgets is interacted with.
|
|
@@ -332,6 +403,23 @@ export const getOrCreateEditor = ({
|
|
|
332
403
|
}),
|
|
333
404
|
);
|
|
334
405
|
|
|
406
|
+
if (name.endsWith('ts') || name.endsWith('tsx')) {
|
|
407
|
+
// Initialize worker if needed
|
|
408
|
+
const tsWorker = await initializeWorker();
|
|
409
|
+
|
|
410
|
+
extensions.push(
|
|
411
|
+
...[
|
|
412
|
+
tsFacetWorker.of({ worker: tsWorker, path: name }),
|
|
413
|
+
tsSyncWorker(),
|
|
414
|
+
tsLinterWorker(),
|
|
415
|
+
autocompletion({
|
|
416
|
+
override: [tsAutocompleteWorker()],
|
|
417
|
+
}),
|
|
418
|
+
tsHoverWorker(),
|
|
419
|
+
],
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
|
|
335
423
|
// Show the minimap
|
|
336
424
|
// See https://github.com/replit/codemirror-minimap#usage
|
|
337
425
|
// This extension has poor performance, so it's disabled for now.
|
|
@@ -382,6 +470,7 @@ export const getOrCreateEditor = ({
|
|
|
382
470
|
const editorCacheValue: ExtendedEditorCacheValue = {
|
|
383
471
|
editor,
|
|
384
472
|
themeCompartment,
|
|
473
|
+
languageCompartment,
|
|
385
474
|
rainbowBracketsCompartment,
|
|
386
475
|
updateRainbowBrackets: (enabled) => {
|
|
387
476
|
editor.dispatch({
|
|
@@ -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.
|
|
@@ -19,9 +21,13 @@ const filesPath = ['files'];
|
|
|
19
21
|
export const CodeEditor = ({
|
|
20
22
|
customInteractRules,
|
|
21
23
|
aiCopilotEndpoint,
|
|
24
|
+
esLintSource,
|
|
22
25
|
}: {
|
|
23
26
|
customInteractRules?: Array<InteractRule>;
|
|
24
27
|
aiCopilotEndpoint?: string;
|
|
28
|
+
esLintSource: (
|
|
29
|
+
view: EditorView,
|
|
30
|
+
) => Promise<readonly Diagnostic[]>;
|
|
25
31
|
}) => {
|
|
26
32
|
const {
|
|
27
33
|
activePane,
|
|
@@ -78,11 +84,17 @@ export const CodeEditor = ({
|
|
|
78
84
|
enableAutoFollowRef.current = enableAutoFollow;
|
|
79
85
|
}, [enableAutoFollow]);
|
|
80
86
|
|
|
87
|
+
// State to hold the editor cache value
|
|
88
|
+
const [editorCacheValue, setEditorCacheValue] =
|
|
89
|
+
useState<EditorCacheValue | null>(null);
|
|
90
|
+
|
|
81
91
|
// Get the editor corresponding to the active file.
|
|
82
92
|
// Looks in `editorCache` first, and if not found, creates a new editor.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
let isMounted = true;
|
|
95
|
+
|
|
96
|
+
const initEditor = async () => {
|
|
97
|
+
const editor = await getOrCreateEditor({
|
|
86
98
|
fileId: activePane.activeFileId,
|
|
87
99
|
shareDBDoc,
|
|
88
100
|
content,
|
|
@@ -97,20 +109,32 @@ export const CodeEditor = ({
|
|
|
97
109
|
enableAutoFollowRef,
|
|
98
110
|
openTab,
|
|
99
111
|
aiCopilotEndpoint,
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
+
]);
|
|
114
138
|
|
|
115
139
|
// Every time the active file switches from one file to another,
|
|
116
140
|
// the editor corresponding to the old file is removed from the DOM,
|
|
@@ -119,6 +143,7 @@ export const CodeEditor = ({
|
|
|
119
143
|
// Guard against cases where page is still loading.
|
|
120
144
|
if (!codeEditorRef.current) return;
|
|
121
145
|
if (!content) return;
|
|
146
|
+
if (!editorCacheValue) return; // Guard against null editorCacheValue
|
|
122
147
|
// if (openSplitPane) return;
|
|
123
148
|
|
|
124
149
|
// Add the editor and apply the prior scroll position to the DOM.
|
|
@@ -132,17 +157,24 @@ export const CodeEditor = ({
|
|
|
132
157
|
return () => {
|
|
133
158
|
// Remove the old editor from the DOM and store the current scroll position.
|
|
134
159
|
// This happens every time `activeFileId` changes.
|
|
135
|
-
|
|
136
|
-
editorCacheValue
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
+
}
|
|
140
172
|
};
|
|
141
173
|
}, [shareDBDoc, editorCacheValue]);
|
|
142
174
|
|
|
143
175
|
// Focus the editor
|
|
144
176
|
useEffect(() => {
|
|
145
|
-
if (editorWantsFocus) {
|
|
177
|
+
if (editorWantsFocus && editorCacheValue) {
|
|
146
178
|
editorCacheValue.editor.focus();
|
|
147
179
|
editorNoLongerWantsFocus();
|
|
148
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
|
+
);
|
|
@@ -113,6 +113,11 @@ export type VZCodeContextValue = {
|
|
|
113
113
|
isSearchOpen: boolean;
|
|
114
114
|
setIsSearchOpen: (isSearchOpen: boolean) => void;
|
|
115
115
|
setSearch: (pattern: string) => void;
|
|
116
|
+
|
|
117
|
+
isAIChatOpen: boolean;
|
|
118
|
+
setIsAIChatOpen: (isAIChatOpen: boolean) => void;
|
|
119
|
+
aiChatFocused: boolean;
|
|
120
|
+
toggleAIChatFocused: () => void;
|
|
116
121
|
setSearchResults: (files: ShareDBDoc<VizContent>) => void;
|
|
117
122
|
setSearchFileVisibility: (
|
|
118
123
|
files: ShareDBDoc<VizContent>,
|
|
@@ -252,6 +257,8 @@ export const VZCodeProvider = ({
|
|
|
252
257
|
theme,
|
|
253
258
|
search,
|
|
254
259
|
isSearchOpen,
|
|
260
|
+
isAIChatOpen,
|
|
261
|
+
aiChatFocused,
|
|
255
262
|
isSettingsOpen,
|
|
256
263
|
isDocOpen,
|
|
257
264
|
editorWantsFocus,
|
|
@@ -260,7 +267,7 @@ export const VZCodeProvider = ({
|
|
|
260
267
|
sidebarPresenceIndicators,
|
|
261
268
|
} = state;
|
|
262
269
|
|
|
263
|
-
const activePane = findPane(pane, activePaneId);
|
|
270
|
+
const activePane: Pane = findPane(pane, activePaneId);
|
|
264
271
|
if (activePane.type !== 'leafPane') {
|
|
265
272
|
// Should never happen
|
|
266
273
|
throw new Error('Expected leafPane');
|
|
@@ -281,6 +288,8 @@ export const VZCodeProvider = ({
|
|
|
281
288
|
setSearchLineVisibility,
|
|
282
289
|
setSearchFocusedIndex,
|
|
283
290
|
toggleSearchFocused,
|
|
291
|
+
setIsAIChatOpen,
|
|
292
|
+
toggleAIChatFocused,
|
|
284
293
|
setIsSettingsOpen,
|
|
285
294
|
setIsDocOpen,
|
|
286
295
|
closeSettings,
|
|
@@ -304,7 +313,7 @@ export const VZCodeProvider = ({
|
|
|
304
313
|
// The set of open directories.
|
|
305
314
|
// TODO move this into reducer/useActions
|
|
306
315
|
const { isDirectoryOpen, toggleDirectory } =
|
|
307
|
-
useOpenDirectories();
|
|
316
|
+
useOpenDirectories({ activePane, content });
|
|
308
317
|
|
|
309
318
|
// Cache of CodeMirror editors by file id.
|
|
310
319
|
const editorCache: EditorCache = useEditorCache();
|
|
@@ -324,6 +333,8 @@ export const VZCodeProvider = ({
|
|
|
324
333
|
submitOperation,
|
|
325
334
|
closeTabs,
|
|
326
335
|
openTab,
|
|
336
|
+
editorCache,
|
|
337
|
+
content,
|
|
327
338
|
});
|
|
328
339
|
|
|
329
340
|
// State to control the create file modal's visibility
|
|
@@ -435,6 +446,11 @@ export const VZCodeProvider = ({
|
|
|
435
446
|
setSearchFocusedIndex,
|
|
436
447
|
toggleSearchFocused,
|
|
437
448
|
|
|
449
|
+
isAIChatOpen,
|
|
450
|
+
setIsAIChatOpen,
|
|
451
|
+
aiChatFocused,
|
|
452
|
+
toggleAIChatFocused,
|
|
453
|
+
|
|
438
454
|
isSettingsOpen,
|
|
439
455
|
setIsSettingsOpen,
|
|
440
456
|
closeSettings,
|
package/src/client/VZMiddle.tsx
CHANGED
|
@@ -8,6 +8,10 @@ import { AIAssistWidget } from './AIAssist/AIAssistWidget';
|
|
|
8
8
|
import { VZCodeContext } from './VZCodeContext';
|
|
9
9
|
import { RunCodeWidget } from './RunCodeWidget';
|
|
10
10
|
import { InteractRule } from '@replit/codemirror-interact';
|
|
11
|
+
import { EditorView } from '@codemirror/view';
|
|
12
|
+
import { Diagnostic } from '@codemirror/lint';
|
|
13
|
+
import { VizContent } from '@vizhub/viz-types';
|
|
14
|
+
import { LeafPane } from '../types';
|
|
11
15
|
|
|
12
16
|
// TODO modify this to handle the SplitPane type
|
|
13
17
|
// Recursive structure?
|
|
@@ -23,6 +27,7 @@ const PaneView = ({
|
|
|
23
27
|
aiAssistTooltipText,
|
|
24
28
|
aiAssistClickOverride,
|
|
25
29
|
aiCopilotEndpoint,
|
|
30
|
+
esLintSource,
|
|
26
31
|
}) => {
|
|
27
32
|
// This prevents the CodeEditor from rendering
|
|
28
33
|
// during SSR.
|
|
@@ -49,6 +54,7 @@ const PaneView = ({
|
|
|
49
54
|
<CodeEditor
|
|
50
55
|
customInteractRules={customInteractRules}
|
|
51
56
|
aiCopilotEndpoint={aiCopilotEndpoint}
|
|
57
|
+
esLintSource={esLintSource}
|
|
52
58
|
/>
|
|
53
59
|
)}
|
|
54
60
|
{isClient &&
|
|
@@ -84,6 +90,7 @@ export const VZMiddle = ({
|
|
|
84
90
|
aiAssistClickOverride,
|
|
85
91
|
aiCopilotEndpoint,
|
|
86
92
|
customInteractRules,
|
|
93
|
+
esLintSource,
|
|
87
94
|
}: {
|
|
88
95
|
enableAIAssist?: boolean;
|
|
89
96
|
aiAssistEndpoint?: string;
|
|
@@ -92,6 +99,9 @@ export const VZMiddle = ({
|
|
|
92
99
|
aiAssistClickOverride?: () => void;
|
|
93
100
|
aiCopilotEndpoint?: string;
|
|
94
101
|
customInteractRules?: Array<InteractRule>;
|
|
102
|
+
esLintSource: (
|
|
103
|
+
view: EditorView,
|
|
104
|
+
) => Promise<readonly Diagnostic[]>;
|
|
95
105
|
}) => {
|
|
96
106
|
const { codeEditorWidth } = useContext(
|
|
97
107
|
SplitPaneResizeContext,
|
|
@@ -128,6 +138,7 @@ export const VZMiddle = ({
|
|
|
128
138
|
aiAssistTooltipText={aiAssistTooltipText}
|
|
129
139
|
aiAssistClickOverride={aiAssistClickOverride}
|
|
130
140
|
aiCopilotEndpoint={aiCopilotEndpoint}
|
|
141
|
+
esLintSource={esLintSource}
|
|
131
142
|
/>
|
|
132
143
|
<CodeErrorOverlay
|
|
133
144
|
errorMessage={errorMessage}
|