vzcode 0.70.0 → 0.72.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 (34) hide show
  1. package/README.md +1 -1
  2. package/dist/assets/{index-Bfl2hCW9.js → index-BfS1mzJK.js} +47 -47
  3. package/dist/assets/index-CpIQ8AoQ.css +1 -0
  4. package/dist/assets/{worker-D0nKlL79.js → worker-CH5jA9Si.js} +44 -44
  5. package/dist/index.html +2 -2
  6. package/package.json +20 -20
  7. package/src/client/AIAssist/AIAssistWidget/index.tsx +10 -3
  8. package/src/client/AIAssist/AIAssistWidget/style.scss +4 -2
  9. package/src/client/AIAssist/index.ts +0 -1
  10. package/src/client/App/index.tsx +2 -0
  11. package/src/client/App/style.scss +6 -0
  12. package/src/client/App/useShareDB.ts +12 -0
  13. package/src/client/CodeEditor/index.tsx +6 -5
  14. package/src/client/CodeEditor/style.scss +4 -0
  15. package/src/client/Fonts/fonts.ts +8 -0
  16. package/src/client/Icons/PlaySVG.tsx +16 -0
  17. package/src/client/Icons/index.tsx +1 -0
  18. package/src/client/RunCodeWidget/index.tsx +53 -0
  19. package/src/client/RunCodeWidget/style.scss +7 -0
  20. package/src/client/TabList/style.scss +4 -4
  21. package/src/client/VZCodeContext.tsx +20 -8
  22. package/src/client/VZMiddle.tsx +2 -0
  23. package/src/client/VZSettings.tsx +96 -7
  24. package/src/client/VZSidebar/index.tsx +8 -4
  25. package/src/client/VZSidebar/styles.scss +13 -11
  26. package/src/client/shouldTriggerRun.ts +0 -2
  27. package/src/client/useKeyboardShortcuts.ts +54 -16
  28. package/src/client/usePrettier/index.ts +34 -102
  29. package/src/client/useRunCode.tsx +28 -0
  30. package/src/client/useTypeScript/worker.ts +8 -1
  31. package/src/server/computeInitialDocument.js +2 -1
  32. package/src/server/index.js +1 -49
  33. package/src/types.ts +10 -0
  34. package/dist/assets/index-DOd2vdTJ.css +0 -1
@@ -1,41 +1,73 @@
1
- import { useCallback, useEffect } from 'react';
1
+ import { useContext, useEffect } from 'react';
2
+ import { shouldTriggerRun } from './shouldTriggerRun';
3
+ import { VZCodeContext } from './VZCodeContext';
2
4
 
5
+ // This module implements the keyboard shortcuts
6
+ // for the VZCode editor.
7
+ // These include:
8
+ // * Alt-w: Close the current tab
9
+ // * Alt-n: Open the create file modal
10
+ // * Alt-PageUp: Change the active tab to the previous one
11
+ // * Alt-PageDown: Change the active tab to the next one
12
+ // * Ctrl-s or Shift-Enter: Run the code and format it with Prettier
3
13
  export const useKeyboardShortcuts = ({
4
14
  closeTabs,
5
15
  activeFileId,
6
16
  handleOpenCreateFileModal,
7
17
  setActiveFileLeft,
8
18
  setActiveFileRight,
19
+ runPrettierRef,
20
+ runCodeRef,
9
21
  }) => {
10
- const handleKeyPress = useCallback(
11
- (event: KeyboardEvent) => {
22
+ useEffect(() => {
23
+ const handleKeyPress = (event: KeyboardEvent) => {
24
+ if (shouldTriggerRun(event)) {
25
+ event.preventDefault();
26
+
27
+ // Run Prettier
28
+ const runPrettier = runPrettierRef.current;
29
+ if (runPrettier !== null) {
30
+ runPrettier();
31
+ }
32
+
33
+ // Run the code
34
+ const runCode = runCodeRef.current;
35
+ if (runCode !== null) {
36
+ runCode();
37
+ }
38
+ return;
39
+ }
40
+
12
41
  if (event.altKey === true) {
42
+ // Alt-w: Close the current tab
13
43
  if (event.key === 'w') {
44
+ // TODO clean this up so we can remove `activeFileId`
45
+ // as a dependency
46
+ // TODO closeActiveTab()
14
47
  closeTabs([activeFileId]);
48
+ return;
15
49
  }
50
+
51
+ // Alt-n: Open the create file modal
16
52
  if (event.key === 'n') {
17
53
  handleOpenCreateFileModal();
54
+ return;
18
55
  }
56
+
57
+ // Alt-PageUp: Change the active tab to the previous one
19
58
  if (event.key === 'PageUp') {
20
- // Change the active tab to the previous one
21
59
  setActiveFileLeft();
60
+ return;
22
61
  }
62
+
63
+ // Alt-PageDown: Change the active tab to the next one
23
64
  if (event.key === 'PageDown') {
24
- // Change the active tab to the next one
25
65
  setActiveFileRight();
66
+ return;
26
67
  }
27
68
  }
28
- },
29
- [
30
- handleOpenCreateFileModal,
31
- closeTabs,
32
- activeFileId,
33
- setActiveFileLeft,
34
- setActiveFileRight,
35
- ],
36
- );
69
+ };
37
70
 
38
- useEffect(() => {
39
71
  document.addEventListener('keydown', handleKeyPress);
40
72
  return () => {
41
73
  document.removeEventListener(
@@ -43,5 +75,11 @@ export const useKeyboardShortcuts = ({
43
75
  handleKeyPress,
44
76
  );
45
77
  };
46
- }, [handleKeyPress]);
78
+ }, [
79
+ handleOpenCreateFileModal,
80
+ closeTabs,
81
+ activeFileId,
82
+ setActiveFileLeft,
83
+ setActiveFileRight,
84
+ ]);
47
85
  };
@@ -31,14 +31,12 @@ export const usePrettier = ({
31
31
  shareDBDoc,
32
32
  submitOperation,
33
33
  prettierWorker,
34
- enableManualPretter,
35
34
  }: {
36
35
  shareDBDoc: ShareDBDoc<VZCodeContent> | null;
37
36
  submitOperation: (
38
37
  next: (content: VZCodeContent) => VZCodeContent,
39
38
  ) => void;
40
39
  prettierWorker: Worker;
41
- enableManualPretter: boolean;
42
40
  }) => {
43
41
  // The set of files that have been modified
44
42
  // since the last Prettier run.
@@ -139,114 +137,48 @@ export const usePrettier = ({
139
137
  };
140
138
  runPrettierRef.current = runPrettier;
141
139
 
142
- let debouncePrettier;
143
- let debounceTimeout;
144
- let handleKeyDown;
145
- if (enableManualPretter) {
146
- handleKeyDown = (event: KeyboardEvent) => {
147
- if (shouldTriggerRun(event)) {
148
- event.preventDefault();
149
- runPrettier();
150
- }
151
- };
152
- document.addEventListener('keydown', handleKeyDown);
153
- } else {
154
- // Function to debounce running Prettier
155
-
156
- debouncePrettier = () => {
157
- clearTimeout(debounceTimeout);
158
- debounceTimeout = setTimeout(
159
- runPrettier,
160
- autoPrettierDebounceTimeMS,
161
- );
162
- };
163
- // Stops prettier from running when you perform a mouse move or a keydown
164
- window.addEventListener(
165
- 'mousemove',
166
- debouncePrettier,
167
- );
168
- window.addEventListener('keydown', debouncePrettier);
169
- }
140
+ const handleOpBatch = (op: JSONOp) => {
141
+ // Only act on changes coming from the client
142
+ // OR changes coming from the AI Assist that we
143
+ // if (isLocal) {
144
+ // If the op is coming from Prettier itself,
145
+ // then do nothing.
146
+ if (isApplyingOpRef.current) {
147
+ // console.log('ignoring op from Prettier');
148
+ return;
149
+ }
150
+
151
+ // Check if the path of this op is the text content of a file
152
+ if (
153
+ op &&
154
+ op.length === 4 &&
155
+ op[0] === 'files' &&
156
+ typeof op[1] === 'string' &&
157
+ op[2] === 'text'
158
+ ) {
159
+ // Get the file id
160
+ const fileId: FileId = op[1];
161
+
162
+ // Add the file id to the set of dirty files
163
+ dirtyFilesRef.current.add(fileId);
164
+ }
165
+ };
170
166
 
171
167
  // Listen for changes
172
- shareDBDoc.on(
173
- 'op batch',
174
- (op: JSONOp, isLocal: boolean) => {
175
- // Only act on changes coming from the client
176
- // OR changes coming from the AI Assist that we
177
- // if (isLocal) {
178
- // If the op is coming from Prettier itself,
179
- // then do nothing.
180
- if (isApplyingOpRef.current) {
181
- // console.log('ignoring op from Prettier');
182
- return;
183
- }
184
-
185
- // Example op that we want to act on:
186
- // op [
187
- // "files",
188
- // "2514857669",
189
- // "text",
190
- // {
191
- // "es": [
192
- // 18,
193
- // "d"
194
- // ]
195
- // }
196
- // ]
197
-
198
- // Check if the path of this op is the text content of a file
199
- if (
200
- op &&
201
- op.length === 4 &&
202
- op[0] === 'files' &&
203
- typeof op[1] === 'string' &&
204
- op[2] === 'text'
205
- ) {
206
- // Get the file id
207
- const fileId: FileId = op[1];
208
-
209
- // Add the file id to the set of dirty files
210
- dirtyFilesRef.current.add(fileId);
211
-
212
- // Debounce running Prettier
213
- if (!enableManualPretter) {
214
- debouncePrettier();
215
- }
216
- // console.log('Op is for file', fileId);
217
- }
218
- // } else {
219
- // // console.log('ignoring op from remote', op, isLocal);
220
- // }
221
- },
222
- );
168
+ shareDBDoc.on('op batch', handleOpBatch);
223
169
 
224
- // In the event that the component unmounts,
170
+ // Clean up the event listeners
225
171
  return () => {
226
- // Remove the event listener
227
172
  prettierWorker.removeEventListener(
228
173
  'message',
229
174
  handleMessage,
230
175
  );
231
- if (enableManualPretter) {
232
- document.removeEventListener(
233
- 'keydown',
234
- handleKeyDown,
235
- );
236
- } else {
237
- window.removeEventListener(
238
- 'keydown',
239
- debouncePrettier,
240
- );
241
- window.removeEventListener(
242
- 'mousemove',
243
- debouncePrettier,
244
- );
245
-
246
- // Clear the timeout
247
- clearTimeout(debounceTimeout);
248
- }
176
+
177
+ shareDBDoc.removeListener('op batch', handleOpBatch);
249
178
  };
250
179
  }, [shareDBDoc]);
251
- return { prettierError, runPrettierRef }; // Return the errors for use in other files
180
+
181
+ // Return the errors and run prettier function ref
182
+ // for use elsewhere.
183
+ return { prettierError, runPrettierRef };
252
184
  };
@@ -0,0 +1,28 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import { SubmitOperation, VZCodeContent } from '../types';
3
+
4
+ // Runs the code by flashing `isInteracting` to `true`.
5
+ export const useRunCode = (
6
+ submitOperation: SubmitOperation,
7
+ ) => {
8
+ const runCodeRef = useRef(null);
9
+ useEffect(() => {
10
+ const runCode = () => {
11
+ submitOperation((content: VZCodeContent) => ({
12
+ ...content,
13
+ isInteracting: true,
14
+ }));
15
+ setTimeout(() => {
16
+ // This somewhat cryptic logic
17
+ // deletes the `isInteracting` property
18
+ // from the document.
19
+ submitOperation(
20
+ ({ isInteracting, ...newDocument }) =>
21
+ newDocument,
22
+ );
23
+ }, 0);
24
+ };
25
+ runCodeRef.current = runCode;
26
+ }, [submitOperation]);
27
+ return runCodeRef;
28
+ };
@@ -240,6 +240,11 @@ onmessage = async ({ data }) => {
240
240
  // of type '"test"' can't be used to index type '{}'."
241
241
  const LINT_ERROR_CODE_ANY_TYPE_KEYS = 7053;
242
242
 
243
+ // "Property 'id' does not exist on type { ... }"
244
+ // Happens on objects with dynamic keys.
245
+ // Not valid in TypeScript, but common in JavaScript.
246
+ const LINT_ERROR_CODE_NON_EXISTENT_PROPERTY = 2339;
247
+
243
248
  if (debug) {
244
249
  console.log(tsErrors);
245
250
  }
@@ -249,7 +254,9 @@ onmessage = async ({ data }) => {
249
254
  error.code !== LINT_ERROR_CODE_IMPORT &&
250
255
  error.code !== LINT_ERROR_CODE_ANY_PARAM &&
251
256
  error.code !== LINT_ERROR_CODE_ANY_TYPE &&
252
- error.code !== LINT_ERROR_CODE_ANY_TYPE_KEYS,
257
+ error.code !== LINT_ERROR_CODE_ANY_TYPE_KEYS &&
258
+ error.code !==
259
+ LINT_ERROR_CODE_NON_EXISTENT_PROPERTY,
253
260
  );
254
261
  }
255
262
  tsErrors = convertToCodeMirrorDiagnostic(tsErrors);
@@ -87,7 +87,8 @@ export const computeInitialDocument = ({ fullPath }) => {
87
87
  // using an interactive code widget such as number dragger.
88
88
  // * When true, the auto-save to the file system is changed to be
89
89
  // more frequent (throttled not debounced).
90
- isInteracting: false,
90
+ // isInteracting: false,
91
+ // This is `undefined` initially.
91
92
  };
92
93
 
93
94
  /**
@@ -206,60 +206,12 @@ function debounceSave() {
206
206
 
207
207
  // Subscribe to listen for modifications
208
208
  shareDBDoc.subscribe(() => {
209
- shareDBDoc.on('op', (op, source) => {
209
+ shareDBDoc.on('op', () => {
210
210
  if (shareDBDoc.data.isInteracting) {
211
211
  throttleSave();
212
212
  } else {
213
213
  debounceSave();
214
214
  }
215
-
216
- // if (
217
- // op !== null &&
218
- // op[0] == 'aiStreams' &&
219
- // source !== 'AIServer' &&
220
- // source !== 'AIAssist'
221
- // ) {
222
- // //This is an insert operation (a new request)
223
- // if (
224
- // op[op.length - 1]['i'] !== undefined &&
225
- // op[op.length - 1]['i']['AIStreamStatus'] !==
226
- // undefined
227
- // ) {
228
- // const input =
229
- // op[op.length - 1]['i']['AIStreamStatus'];
230
-
231
- // generateAIResponse({
232
- // inputText: input.text,
233
- // insertionCursor: input.insertionCursor,
234
- // shareDBDoc: shareDBDoc,
235
- // streamId: op[op.length - 2],
236
- // fileId: input.fileId,
237
- // });
238
-
239
- // const confirmStartOperation = replaceOp(
240
- // [
241
- // ...op.filter(
242
- // (value) => typeof value === 'string',
243
- // ),
244
- // 'AIStreamStatus',
245
- // 'serverIsRunning',
246
- // ],
247
- // true,
248
- // true,
249
- // );
250
-
251
- // shareDBDoc.submitOp(confirmStartOperation, {
252
- // source: 'AIServer',
253
- // });
254
- // }
255
- // if (
256
- // op[op.length - 1]['r'] !== null &&
257
- // op[op.length - 2] == 'clientWantsToStart'
258
- // ) {
259
- // //client wants to halt generation
260
- // haltGeneration(op[1]);
261
- // }
262
- // }
263
215
  });
264
216
  });
265
217
 
package/src/types.ts CHANGED
@@ -76,6 +76,10 @@ export type ShareDBDoc<T> = {
76
76
  event: string,
77
77
  callback: (op: JSONOp, source: boolean) => void,
78
78
  ) => void;
79
+ removeListener: (
80
+ event: string,
81
+ callback: (op: JSONOp, source: boolean) => void,
82
+ ) => void;
79
83
  submitOp: (
80
84
  op: JSONOp,
81
85
  options?: any,
@@ -177,3 +181,9 @@ export type PresenceId = string;
177
181
 
178
182
  // A user name for display in presence.
179
183
  export type Username = string;
184
+
185
+ // The type of a convenience function called `submitOperation`,
186
+ // to submit an operation to ShareDB.
187
+ export type SubmitOperation = (
188
+ next: (content: VZCodeContent) => VZCodeContent,
189
+ ) => void;