vzcode 0.28.0 → 0.29.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/assets/index-836bdce7.css +1 -0
- package/dist/assets/index-c7f894a9.js +115 -0
- package/dist/assets/worker-a2979249.js +366 -0
- package/dist/index.html +2 -2
- package/package.json +10 -6
- package/src/client/AIAssist.ts +110 -19
- package/src/client/AIAssistWidget/index.tsx +101 -0
- package/src/client/AIAssistWidget/style.scss +10 -0
- package/src/client/App/VZCodeContext.tsx +251 -0
- package/src/client/App/VZLeft.tsx +56 -0
- package/src/client/App/VZMiddle.tsx +91 -0
- package/src/client/App/VZRight.tsx +15 -0
- package/src/client/App/index.tsx +104 -0
- package/src/client/{style.scss → App/style.scss} +14 -3
- package/src/client/App/useShareDB.ts +103 -0
- package/src/client/CodeEditor/getOrCreateEditor.ts +54 -6
- package/src/client/CodeEditor/index.tsx +9 -0
- package/src/client/CodeEditor/json1PresenceDisplay.ts +38 -9
- package/src/client/CodeEditor/typeScriptCompletions.ts +108 -0
- package/src/client/CodeEditor/typeScriptLinter.ts +45 -0
- package/src/client/CodeErrorOverlay/index.tsx +27 -1
- package/src/client/CodeErrorOverlay/style.scss +11 -11
- package/src/client/SplitPaneResizeContext.tsx +195 -39
- package/src/client/TabList/index.tsx +1 -27
- package/src/client/{Resizer → VZResizer}/index.tsx +37 -13
- package/src/client/VZSettings.tsx +1 -1
- package/src/client/VZSidebar/CreateFileModal.tsx +93 -0
- package/src/client/VZSidebar/DeleteConfirmationModal.tsx +42 -24
- package/src/client/VZSidebar/index.tsx +69 -9
- package/src/client/useFileCRUD.ts +19 -19
- package/src/client/useOpenDirectories.ts +12 -8
- package/src/client/usePrettier/index.ts +13 -1
- package/src/client/useTypeScript/index.ts +44 -0
- package/src/client/useTypeScript/requestTypes.ts +29 -0
- package/src/client/useTypeScript/worker.ts +222 -0
- package/src/client/usernameLocalStorage.ts +1 -1
- package/src/client/vzReducer/createInitialState.ts +4 -5
- package/src/index.ts +5 -1
- package/src/server/handleAIAssist.js +76 -12
- package/src/server/index.js +55 -2
- package/src/types.ts +78 -1
- package/dist/assets/index-a9585cbc.js +0 -111
- package/dist/assets/index-d30c9f1b.css +0 -1
- package/src/client/App.tsx +0 -286
- /package/src/client/{Resizer → VZResizer}/styles.scss +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useState,
|
|
3
|
+
useCallback,
|
|
4
|
+
useRef,
|
|
5
|
+
useEffect,
|
|
6
|
+
} from 'react';
|
|
7
|
+
import { Modal, Form, Button } from '../bootstrap';
|
|
8
|
+
|
|
9
|
+
export const CreateFileModal = ({
|
|
10
|
+
show,
|
|
11
|
+
onClose,
|
|
12
|
+
onRename,
|
|
13
|
+
initialFileName = '',
|
|
14
|
+
}) => {
|
|
15
|
+
const [newName, setNewName] = useState(initialFileName);
|
|
16
|
+
const inputRef = useRef(null);
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (show && inputRef.current) {
|
|
20
|
+
inputRef.current.focus();
|
|
21
|
+
}
|
|
22
|
+
}, [show]);
|
|
23
|
+
|
|
24
|
+
const handleNameChange = useCallback((event) => {
|
|
25
|
+
setNewName(event.target.value);
|
|
26
|
+
}, []);
|
|
27
|
+
|
|
28
|
+
const handleRenameClick = useCallback(() => {
|
|
29
|
+
onRename(newName);
|
|
30
|
+
setNewName('');
|
|
31
|
+
}, [newName, onRename]);
|
|
32
|
+
|
|
33
|
+
// Returns true if file name is valid, false otherwise.
|
|
34
|
+
const validateFileName = useCallback(
|
|
35
|
+
(fileName: string) => {
|
|
36
|
+
const regex =
|
|
37
|
+
/^[a-zA-Z0-9](?:[a-zA-Z0-9 ./+=_-]*[a-zA-Z0-9])?$/;
|
|
38
|
+
return regex.test(fileName);
|
|
39
|
+
},
|
|
40
|
+
[],
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const handleKeyDown = useCallback(
|
|
44
|
+
(e) => {
|
|
45
|
+
if (
|
|
46
|
+
e.key === 'Enter' &&
|
|
47
|
+
(e.ctrlKey ||
|
|
48
|
+
e.shiftKey ||
|
|
49
|
+
(!e.altKey && !e.metaKey))
|
|
50
|
+
) {
|
|
51
|
+
handleRenameClick();
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
[handleRenameClick],
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return show ? (
|
|
58
|
+
<Modal
|
|
59
|
+
show={show}
|
|
60
|
+
onHide={onClose}
|
|
61
|
+
animation={false}
|
|
62
|
+
onKeyDown={handleKeyDown}
|
|
63
|
+
>
|
|
64
|
+
<Modal.Header closeButton>
|
|
65
|
+
<Modal.Title>Create File</Modal.Title>
|
|
66
|
+
</Modal.Header>
|
|
67
|
+
<Modal.Body>
|
|
68
|
+
<Form.Group className="mb-3" controlId="fileName">
|
|
69
|
+
<Form.Label>File Name</Form.Label>
|
|
70
|
+
<Form.Control
|
|
71
|
+
type="text"
|
|
72
|
+
value={newName}
|
|
73
|
+
onChange={handleNameChange}
|
|
74
|
+
ref={inputRef}
|
|
75
|
+
spellCheck="false"
|
|
76
|
+
/>
|
|
77
|
+
<Form.Text className="text-muted">
|
|
78
|
+
Enter the name for your file
|
|
79
|
+
</Form.Text>
|
|
80
|
+
</Form.Group>
|
|
81
|
+
</Modal.Body>
|
|
82
|
+
<Modal.Footer>
|
|
83
|
+
<Button
|
|
84
|
+
variant="primary"
|
|
85
|
+
onClick={handleRenameClick}
|
|
86
|
+
disabled={!validateFileName(newName)}
|
|
87
|
+
>
|
|
88
|
+
Create File
|
|
89
|
+
</Button>
|
|
90
|
+
</Modal.Footer>
|
|
91
|
+
</Modal>
|
|
92
|
+
) : null;
|
|
93
|
+
};
|
|
@@ -1,36 +1,54 @@
|
|
|
1
1
|
import { Button, Modal } from '../bootstrap';
|
|
2
|
+
import { useCallback } from 'react';
|
|
2
3
|
|
|
3
4
|
// Confirmation modal for deleting a file or directory.
|
|
4
5
|
// Inspired by https://react-bootstrap.netlify.app/docs/components/modal/#live-demo
|
|
6
|
+
|
|
5
7
|
export const DeleteConfirmationModal = ({
|
|
6
8
|
show,
|
|
7
9
|
onClose,
|
|
8
10
|
onConfirm,
|
|
9
11
|
isDirectory,
|
|
10
12
|
name,
|
|
11
|
-
}) =>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
}) => {
|
|
14
|
+
const handleSubmit = useCallback(
|
|
15
|
+
(event) => {
|
|
16
|
+
event.preventDefault();
|
|
17
|
+
onConfirm();
|
|
18
|
+
},
|
|
19
|
+
[onConfirm],
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Modal show={show} onHide={onClose} centered>
|
|
24
|
+
<form onSubmit={handleSubmit}>
|
|
25
|
+
<Modal.Header closeButton onClick={onClose}>
|
|
26
|
+
<Modal.Title>
|
|
27
|
+
Delete {isDirectory ? 'Directory' : 'File'}
|
|
28
|
+
</Modal.Title>
|
|
29
|
+
</Modal.Header>
|
|
18
30
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
<Modal.Body>
|
|
32
|
+
<p>
|
|
33
|
+
This will delete the{' '}
|
|
34
|
+
{isDirectory ? 'directory' : 'file'}{' '}
|
|
35
|
+
<strong>{name}</strong>.
|
|
36
|
+
</p>
|
|
37
|
+
</Modal.Body>
|
|
26
38
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
<Modal.Footer>
|
|
40
|
+
<Button
|
|
41
|
+
variant="secondary"
|
|
42
|
+
type="button"
|
|
43
|
+
onClick={onClose}
|
|
44
|
+
>
|
|
45
|
+
Cancel
|
|
46
|
+
</Button>
|
|
47
|
+
<Button variant="primary" type="submit">
|
|
48
|
+
Delete
|
|
49
|
+
</Button>
|
|
50
|
+
</Modal.Footer>
|
|
51
|
+
</form>
|
|
52
|
+
</Modal>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
useCallback,
|
|
3
|
+
useContext,
|
|
4
|
+
useMemo,
|
|
5
|
+
useState,
|
|
6
|
+
useEffect,
|
|
7
|
+
} from 'react';
|
|
2
8
|
import {
|
|
3
9
|
FileId,
|
|
4
10
|
FileTree,
|
|
@@ -11,14 +17,15 @@ import { sortFileTree } from '../sortFileTree';
|
|
|
11
17
|
import { disableSettings } from '../featureFlags';
|
|
12
18
|
import { SplitPaneResizeContext } from '../SplitPaneResizeContext';
|
|
13
19
|
import { Listing } from './Listing';
|
|
20
|
+
import { CreateFileModal } from './CreateFileModal';
|
|
14
21
|
import './styles.scss';
|
|
15
22
|
|
|
16
|
-
const CreateFileButton = ({
|
|
23
|
+
const CreateFileButton = ({ handleCreateFile }) => {
|
|
17
24
|
return (
|
|
18
25
|
<i
|
|
19
26
|
className="bx bxs-file-plus new-btn"
|
|
20
27
|
style={{ color: '#dbdde1' }}
|
|
21
|
-
onClick={
|
|
28
|
+
onClick={handleCreateFile}
|
|
22
29
|
// TODO better tooltip
|
|
23
30
|
title="Create file"
|
|
24
31
|
></i>
|
|
@@ -31,13 +38,14 @@ export const VZSidebar = ({
|
|
|
31
38
|
deleteFile,
|
|
32
39
|
deleteDirectory,
|
|
33
40
|
openTab,
|
|
41
|
+
closeTabs,
|
|
34
42
|
setIsSettingsOpen,
|
|
35
43
|
isDirectoryOpen,
|
|
36
44
|
toggleDirectory,
|
|
37
45
|
activeFileId,
|
|
38
46
|
}: {
|
|
39
47
|
files: Files;
|
|
40
|
-
createFile: () => void;
|
|
48
|
+
createFile: (fileName) => void;
|
|
41
49
|
renameFile: (fileId: FileId, newName: string) => void;
|
|
42
50
|
deleteFile: (fileId: FileId) => void;
|
|
43
51
|
deleteDirectory: (path: FileTreePath) => void;
|
|
@@ -48,6 +56,7 @@ export const VZSidebar = ({
|
|
|
48
56
|
fileId: FileId;
|
|
49
57
|
isTransient?: boolean;
|
|
50
58
|
}) => void;
|
|
59
|
+
closeTabs: (fileIds: FileId[]) => void;
|
|
51
60
|
setIsSettingsOpen: (isSettingsOpen: boolean) => void;
|
|
52
61
|
isDirectoryOpen: (path: string) => boolean;
|
|
53
62
|
toggleDirectory: (path: string) => void;
|
|
@@ -58,11 +67,53 @@ export const VZSidebar = ({
|
|
|
58
67
|
[files],
|
|
59
68
|
);
|
|
60
69
|
|
|
70
|
+
const [isModalOpen, setIsModalOpen] = useState(false); // State to control the modal's visibility
|
|
71
|
+
|
|
72
|
+
const handleCreateFile = useCallback(() => {
|
|
73
|
+
setIsModalOpen(true);
|
|
74
|
+
}, [setIsModalOpen]);
|
|
75
|
+
|
|
76
|
+
const handleCloseModal = useCallback(() => {
|
|
77
|
+
setIsModalOpen(false);
|
|
78
|
+
}, [setIsModalOpen]);
|
|
79
|
+
|
|
80
|
+
const handleRename = useCallback(
|
|
81
|
+
(newFileName: string) => {
|
|
82
|
+
createFile(newFileName);
|
|
83
|
+
setIsModalOpen(false);
|
|
84
|
+
},
|
|
85
|
+
[createFile, setIsModalOpen],
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const handleKeyPress = useCallback(
|
|
89
|
+
(event: { altKey: boolean; key: string }) => {
|
|
90
|
+
if (event.altKey == true) {
|
|
91
|
+
if (event.key == 'w') {
|
|
92
|
+
closeTabs([activeFileId]);
|
|
93
|
+
}
|
|
94
|
+
if (event.key == 'n') {
|
|
95
|
+
handleCreateFile();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
[createFile, closeTabs, activeFileId],
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
document.addEventListener('keydown', handleKeyPress);
|
|
104
|
+
return () => {
|
|
105
|
+
document.removeEventListener(
|
|
106
|
+
'keydown',
|
|
107
|
+
handleKeyPress,
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
}, [handleKeyPress]);
|
|
111
|
+
|
|
61
112
|
const handleSettingsClick = useCallback(() => {
|
|
62
113
|
setIsSettingsOpen(true);
|
|
63
114
|
}, []);
|
|
64
115
|
|
|
65
|
-
const {
|
|
116
|
+
const { sidebarWidth } = useContext(
|
|
66
117
|
SplitPaneResizeContext,
|
|
67
118
|
);
|
|
68
119
|
|
|
@@ -91,13 +142,15 @@ export const VZSidebar = ({
|
|
|
91
142
|
return (
|
|
92
143
|
<div
|
|
93
144
|
className="vz-sidebar"
|
|
94
|
-
style={{ width:
|
|
145
|
+
style={{ width: sidebarWidth + 'px' }}
|
|
95
146
|
>
|
|
96
147
|
<div className="files">
|
|
97
148
|
<div className="full-box">
|
|
98
149
|
<div className="sidebar-section-hint">Files</div>
|
|
99
150
|
<div>
|
|
100
|
-
<CreateFileButton
|
|
151
|
+
<CreateFileButton
|
|
152
|
+
handleCreateFile={handleCreateFile}
|
|
153
|
+
/>
|
|
101
154
|
</div>
|
|
102
155
|
</div>
|
|
103
156
|
{filesExist ? (
|
|
@@ -130,7 +183,9 @@ export const VZSidebar = ({
|
|
|
130
183
|
your first file.
|
|
131
184
|
</div>
|
|
132
185
|
|
|
133
|
-
<CreateFileButton
|
|
186
|
+
<CreateFileButton
|
|
187
|
+
handleCreateFile={handleCreateFile}
|
|
188
|
+
/>
|
|
134
189
|
</div>
|
|
135
190
|
)}
|
|
136
191
|
</div>
|
|
@@ -139,9 +194,14 @@ export const VZSidebar = ({
|
|
|
139
194
|
className="settings"
|
|
140
195
|
onClick={handleSettingsClick}
|
|
141
196
|
>
|
|
142
|
-
Settings
|
|
197
|
+
Editor Settings
|
|
143
198
|
</div>
|
|
144
199
|
)}
|
|
200
|
+
<CreateFileModal
|
|
201
|
+
show={isModalOpen}
|
|
202
|
+
onClose={handleCloseModal}
|
|
203
|
+
onRename={handleRename}
|
|
204
|
+
/>
|
|
145
205
|
</div>
|
|
146
206
|
);
|
|
147
207
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback } from 'react';
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
FileId,
|
|
4
4
|
FileTreePath,
|
|
@@ -26,24 +26,24 @@ export const useFileCRUD = ({
|
|
|
26
26
|
}) => void;
|
|
27
27
|
}) => {
|
|
28
28
|
// Create a new file
|
|
29
|
-
const createFile = useCallback(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
29
|
+
const createFile = useCallback(
|
|
30
|
+
(name: string) => {
|
|
31
|
+
if (name) {
|
|
32
|
+
const fileId: FileId = randomId();
|
|
33
|
+
submitOperation((document: VZCodeContent) => ({
|
|
34
|
+
...document,
|
|
35
|
+
files: {
|
|
36
|
+
...document.files,
|
|
37
|
+
[fileId]: { name, text: '' },
|
|
38
|
+
},
|
|
39
|
+
}));
|
|
40
|
+
// When a new file is created, open it in a new tab
|
|
41
|
+
// and focus the editor on it.
|
|
42
|
+
openTab({ fileId, isTransient: false });
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
[submitOperation],
|
|
46
|
+
);
|
|
47
47
|
|
|
48
48
|
// Called when a file in the sidebar is renamed.
|
|
49
49
|
const renameFile = useCallback(
|
|
@@ -15,17 +15,22 @@ import { useState, useCallback } from 'react';
|
|
|
15
15
|
// return openDirectories;
|
|
16
16
|
//};
|
|
17
17
|
|
|
18
|
+
type VZPath = string;
|
|
19
|
+
|
|
18
20
|
// Inspired by
|
|
19
21
|
// https://github.com/vizhub-core/vizhub/blob/main/vizhub-v2/packages/neoFrontend/src/pages/VizPage/Body/Editor/FilesSection/useOpenDirectories.js
|
|
20
22
|
// TODO move this into reducer
|
|
21
|
-
export const useOpenDirectories = ()
|
|
23
|
+
export const useOpenDirectories = (): {
|
|
24
|
+
isDirectoryOpen: (path: VZPath) => boolean;
|
|
25
|
+
toggleDirectory: (path: VZPath) => void;
|
|
26
|
+
} => {
|
|
22
27
|
// The set of open directories by path/
|
|
23
28
|
const [openDirectories, setOpenDirectories] = useState<
|
|
24
|
-
Set<
|
|
29
|
+
Set<VZPath>
|
|
25
30
|
>(new Set());
|
|
26
31
|
|
|
27
32
|
// Whether a directory is open.
|
|
28
|
-
const isDirectoryOpen: (path:
|
|
33
|
+
const isDirectoryOpen: (path: VZPath) => boolean =
|
|
29
34
|
useCallback(
|
|
30
35
|
(path) => openDirectories.has(path),
|
|
31
36
|
[openDirectories],
|
|
@@ -37,21 +42,20 @@ export const useOpenDirectories = () => {
|
|
|
37
42
|
* If it's closed, it will open it.
|
|
38
43
|
*/
|
|
39
44
|
const toggleDirectory = useCallback(
|
|
40
|
-
(
|
|
45
|
+
(path: VZPath) => {
|
|
41
46
|
// Create a copy of the currently open directories
|
|
42
47
|
const updatedOpenDirectories = new Set(
|
|
43
48
|
openDirectories,
|
|
44
49
|
);
|
|
45
50
|
|
|
46
51
|
// Check if the directory is currently open
|
|
47
|
-
const directoryIsOpen =
|
|
48
|
-
isDirectoryOpen(directoryPath);
|
|
52
|
+
const directoryIsOpen = isDirectoryOpen(path);
|
|
49
53
|
|
|
50
54
|
// Toggle the directory's state
|
|
51
55
|
if (directoryIsOpen) {
|
|
52
|
-
updatedOpenDirectories.delete(
|
|
56
|
+
updatedOpenDirectories.delete(path);
|
|
53
57
|
} else {
|
|
54
|
-
updatedOpenDirectories.add(
|
|
58
|
+
updatedOpenDirectories.add(path);
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
// Update the state with the modified set of open directories
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import { useEffect, useRef, useState } from 'react';
|
|
8
8
|
|
|
9
9
|
// The time in milliseconds by which auto-saving is debounced.
|
|
10
|
-
const autoPrettierDebounceTimeMS = 1200;
|
|
10
|
+
export const autoPrettierDebounceTimeMS = 1200;
|
|
11
11
|
|
|
12
12
|
// Computes a file extension from a file name.
|
|
13
13
|
// Example: 'foo.js' => '.js'
|
|
@@ -128,6 +128,10 @@ export const usePrettier = (
|
|
|
128
128
|
);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
//Stops prettier from running when you perform a mouse move or a keydown
|
|
132
|
+
window.addEventListener('mousemove', debouncePrettier);
|
|
133
|
+
window.addEventListener('keydown', debouncePrettier);
|
|
134
|
+
|
|
131
135
|
// Listen for changes
|
|
132
136
|
shareDBDoc.on(
|
|
133
137
|
'op batch',
|
|
@@ -186,6 +190,14 @@ export const usePrettier = (
|
|
|
186
190
|
'message',
|
|
187
191
|
handleMessage,
|
|
188
192
|
);
|
|
193
|
+
window.removeEventListener(
|
|
194
|
+
'keydown',
|
|
195
|
+
debouncePrettier,
|
|
196
|
+
);
|
|
197
|
+
window.removeEventListener(
|
|
198
|
+
'mousemove',
|
|
199
|
+
debouncePrettier,
|
|
200
|
+
);
|
|
189
201
|
|
|
190
202
|
// Clear the timeout
|
|
191
203
|
clearTimeout(debounceTimeout);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
import { VZCodeContent } from '../../types';
|
|
3
|
+
import { autoPrettierDebounceTimeMS } from '../usePrettier';
|
|
4
|
+
|
|
5
|
+
// We don't want to send the message _before_ Prettier runs,
|
|
6
|
+
// so we need to wait at least as long as Prettier takes to run,
|
|
7
|
+
// plus some "wiggle room" which is amount of time it may take
|
|
8
|
+
// to aactually run Prettier and update the document.
|
|
9
|
+
const wiggleRoom = 500;
|
|
10
|
+
|
|
11
|
+
// The time in milliseconds by which auto-saving is debounced.
|
|
12
|
+
const sendMessageDebounceTimeMS =
|
|
13
|
+
autoPrettierDebounceTimeMS + wiggleRoom;
|
|
14
|
+
|
|
15
|
+
export const useTypeScript = ({
|
|
16
|
+
content,
|
|
17
|
+
typeScriptWorker,
|
|
18
|
+
}: {
|
|
19
|
+
content: VZCodeContent;
|
|
20
|
+
typeScriptWorker: Worker;
|
|
21
|
+
}) => {
|
|
22
|
+
// When Content changes, update the TypeScript worker
|
|
23
|
+
// with the new content, but debounced.
|
|
24
|
+
|
|
25
|
+
// This keeps track of the setTimeout ID across renders.
|
|
26
|
+
const debounceTimeoutId = useRef<number | null>(null);
|
|
27
|
+
|
|
28
|
+
const debounceUpdateContent = useCallback(
|
|
29
|
+
(content: VZCodeContent) => {
|
|
30
|
+
clearTimeout(debounceTimeoutId.current);
|
|
31
|
+
debounceTimeoutId.current = window.setTimeout(() => {
|
|
32
|
+
typeScriptWorker.postMessage({
|
|
33
|
+
event: 'update-content',
|
|
34
|
+
details: content,
|
|
35
|
+
});
|
|
36
|
+
}, sendMessageDebounceTimeMS);
|
|
37
|
+
},
|
|
38
|
+
[typeScriptWorker],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
debounceUpdateContent(content);
|
|
43
|
+
}, [content]);
|
|
44
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { VZCodeContent } from '../../types';
|
|
3
|
+
|
|
4
|
+
export type AutocompleteRequest = {
|
|
5
|
+
event: 'autocomplete-request';
|
|
6
|
+
fileName: string;
|
|
7
|
+
fileContent: string;
|
|
8
|
+
position: number;
|
|
9
|
+
requestId: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type AutocompleteResponse = {
|
|
13
|
+
event: 'post-completions';
|
|
14
|
+
completions: any;
|
|
15
|
+
requestId: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type LinterRequest = {
|
|
19
|
+
event: 'lint-request';
|
|
20
|
+
fileName: string;
|
|
21
|
+
fileContent: string;
|
|
22
|
+
requestId: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type LinterResponse = {
|
|
26
|
+
event: 'post-error-linter';
|
|
27
|
+
tsErrors: ts.Diagnostic[];
|
|
28
|
+
requestId: string;
|
|
29
|
+
};
|