vzcode 0.26.0 → 0.28.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 +1 -1
- package/dist/assets/index-a9585cbc.js +111 -0
- package/dist/assets/index-d30c9f1b.css +1 -0
- package/dist/index.html +2 -2
- package/package.json +6 -4
- package/src/client/AIAssist.ts +46 -0
- package/src/client/App.tsx +86 -43
- package/src/client/CodeEditor/getOrCreateEditor.ts +22 -2
- package/src/client/CodeEditor/index.tsx +75 -12
- package/src/client/CodeEditor/json1PresenceBroadcast.ts +6 -2
- package/src/client/CodeEditor/json1PresenceDisplay.ts +0 -45
- package/src/client/CodeEditor/style.scss +5 -1
- package/src/client/CodeErrorOverlay/index.tsx +37 -0
- package/src/client/CodeErrorOverlay/style.scss +41 -0
- package/src/client/PresenceNotifications/PresenceNotifications.test.ts +134 -0
- package/src/client/PresenceNotifications/index.tsx +118 -103
- package/src/client/SplitPaneResizeContext.tsx +12 -7
- package/src/client/TabList/Tab.tsx +20 -6
- package/src/client/TabList/index.tsx +18 -8
- package/src/client/TabList/style.scss +5 -0
- package/src/client/VZSettings.tsx +33 -20
- package/src/client/VZSidebar/{ConfirmationBox.tsx → DeleteConfirmationModal.tsx} +17 -14
- package/src/client/VZSidebar/DirectoryListing.tsx +27 -26
- package/src/client/VZSidebar/FileListing.tsx +16 -19
- package/src/client/VZSidebar/Item.tsx +83 -37
- package/src/client/VZSidebar/Listing.tsx +17 -14
- package/src/client/VZSidebar/index.tsx +87 -45
- package/src/client/VZSidebar/styles.scss +21 -1
- package/src/client/style.scss +3 -0
- package/src/client/useActions.ts +25 -6
- package/src/client/useEditorCache.ts +2 -1
- package/src/client/useFileCRUD.ts +29 -27
- package/src/client/usernameLocalStorage.ts +43 -0
- package/src/client/vzReducer/closeTabReducer.test.ts +177 -0
- package/src/client/vzReducer/closeTabsReducer.ts +68 -0
- package/src/client/vzReducer/createInitialState.ts +19 -0
- package/src/client/vzReducer/editorNoLongerWantsFocusReducer.ts +9 -0
- package/src/client/vzReducer/index.ts +107 -0
- package/src/client/vzReducer/openTabReducer.test.ts +110 -0
- package/src/client/vzReducer/openTabReducer.ts +57 -0
- package/src/client/vzReducer/setActiveFileIdReducer.ts +9 -0
- package/src/client/vzReducer/setIsSettingsOpenReducer.ts +9 -0
- package/src/client/vzReducer/setThemeReducer.ts +9 -0
- package/src/client/vzReducer/setUsernameReducer.ts +9 -0
- package/src/index.ts +9 -3
- package/src/server/handleAIAssist.js +69 -0
- package/src/server/index.js +9 -0
- package/src/types.ts +12 -5
- package/dist/assets/index-284b7339.js +0 -111
- package/dist/assets/index-e160f758.css +0 -1
- package/src/client/PrettierErrorOverlay/index.tsx +0 -9
- package/src/client/PrettierErrorOverlay/style.scss +0 -9
- package/src/client/vzReducer.ts +0 -118
package/src/client/App.tsx
CHANGED
|
@@ -1,33 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useState,
|
|
3
|
-
useEffect,
|
|
4
|
-
useCallback,
|
|
5
|
-
useRef,
|
|
6
|
-
useReducer,
|
|
7
|
-
} from 'react';
|
|
1
|
+
import { useState, useEffect, useReducer } from 'react';
|
|
8
2
|
import ShareDBClient from 'sharedb-client-browser/dist/sharedb-client-umd.cjs';
|
|
9
3
|
import { json1Presence } from '../ot';
|
|
10
4
|
import { randomId } from '../randomId';
|
|
11
5
|
import { CodeEditor } from './CodeEditor';
|
|
12
6
|
import { VZSettings } from './VZSettings';
|
|
13
7
|
import { VZSidebar } from './VZSidebar';
|
|
14
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
Files,
|
|
10
|
+
ShareDBDoc,
|
|
11
|
+
Username,
|
|
12
|
+
VZCodeContent,
|
|
13
|
+
} from '../types';
|
|
15
14
|
import { TabList } from './TabList';
|
|
16
15
|
import { useOpenDirectories } from './useOpenDirectories';
|
|
17
16
|
import { defaultTheme, useDynamicTheme } from './themes';
|
|
18
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
EditorCache,
|
|
19
|
+
useEditorCache,
|
|
20
|
+
} from './useEditorCache';
|
|
19
21
|
import { usePrettier } from './usePrettier';
|
|
20
22
|
// @ts-ignore
|
|
21
23
|
import PrettierWorker from './usePrettier/worker?worker';
|
|
22
24
|
import { SplitPaneResizeProvider } from './SplitPaneResizeContext';
|
|
23
25
|
import { Resizer } from './Resizer';
|
|
24
26
|
import { PresenceNotifications } from './PresenceNotifications';
|
|
25
|
-
import {
|
|
26
|
-
import { vzReducer } from './vzReducer';
|
|
27
|
+
import { CodeErrorOverlay } from './CodeErrorOverlay';
|
|
28
|
+
import { vzReducer, createInitialState } from './vzReducer';
|
|
27
29
|
import { useActions } from './useActions';
|
|
28
30
|
import { useFileCRUD } from './useFileCRUD';
|
|
29
|
-
import './style.scss';
|
|
30
31
|
import { useSubmitOperation } from './useSubmitOperation';
|
|
32
|
+
import {
|
|
33
|
+
useInitialUsername,
|
|
34
|
+
usePersistUsername,
|
|
35
|
+
} from './usernameLocalStorage';
|
|
36
|
+
import './style.scss';
|
|
31
37
|
|
|
32
38
|
// Instantiate the Prettier worker.
|
|
33
39
|
const prettierWorker = new PrettierWorker();
|
|
@@ -53,17 +59,6 @@ function App() {
|
|
|
53
59
|
const [shareDBDoc, setShareDBDoc] =
|
|
54
60
|
useState<ShareDBDoc<VZCodeContent> | null>(null);
|
|
55
61
|
|
|
56
|
-
const submitOperation: (
|
|
57
|
-
next: (content: VZCodeContent) => VZCodeContent,
|
|
58
|
-
) => void = useSubmitOperation(shareDBDoc);
|
|
59
|
-
|
|
60
|
-
// Auto-run Pretter after local changes.
|
|
61
|
-
const { prettierError } = usePrettier(
|
|
62
|
-
shareDBDoc,
|
|
63
|
-
submitOperation,
|
|
64
|
-
prettierWorker,
|
|
65
|
-
);
|
|
66
|
-
|
|
67
62
|
// Local ShareDB presence, for broadcasting our cursor position
|
|
68
63
|
// so other clients can see it.
|
|
69
64
|
// See https://share.github.io/sharedb/api/local-presence
|
|
@@ -127,7 +122,14 @@ function App() {
|
|
|
127
122
|
});
|
|
128
123
|
|
|
129
124
|
// Set up our local presence for broadcasting this client's presence.
|
|
130
|
-
|
|
125
|
+
const generateTimestampedId = () => {
|
|
126
|
+
const timestamp = Date.now().toString(36);
|
|
127
|
+
const randomPart = randomId();
|
|
128
|
+
return `${timestamp}-${randomPart}`;
|
|
129
|
+
};
|
|
130
|
+
setLocalPresence(
|
|
131
|
+
docPresence.create(generateTimestampedId()),
|
|
132
|
+
);
|
|
131
133
|
|
|
132
134
|
// Store docPresence so child components can listen for changes.
|
|
133
135
|
setDocPresence(docPresence);
|
|
@@ -141,17 +143,40 @@ function App() {
|
|
|
141
143
|
};
|
|
142
144
|
}, []);
|
|
143
145
|
|
|
146
|
+
// Get the initial username from localStorage.
|
|
147
|
+
const initialUsername: Username = useInitialUsername();
|
|
148
|
+
|
|
149
|
+
const submitOperation: (
|
|
150
|
+
next: (content: VZCodeContent) => VZCodeContent,
|
|
151
|
+
) => void = useSubmitOperation(shareDBDoc);
|
|
152
|
+
|
|
153
|
+
// Auto-run Pretter after local changes.
|
|
154
|
+
const {
|
|
155
|
+
prettierError,
|
|
156
|
+
}: {
|
|
157
|
+
prettierError: string | null;
|
|
158
|
+
} = usePrettier(
|
|
159
|
+
shareDBDoc,
|
|
160
|
+
submitOperation,
|
|
161
|
+
prettierWorker,
|
|
162
|
+
);
|
|
163
|
+
|
|
144
164
|
// https://react.dev/reference/react/useReducer
|
|
145
|
-
const [state, dispatch] = useReducer(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
});
|
|
165
|
+
const [state, dispatch] = useReducer(
|
|
166
|
+
vzReducer,
|
|
167
|
+
{ defaultTheme, initialUsername },
|
|
168
|
+
createInitialState,
|
|
169
|
+
);
|
|
151
170
|
|
|
152
171
|
// Unpack state.
|
|
153
|
-
const {
|
|
154
|
-
|
|
172
|
+
const {
|
|
173
|
+
tabList,
|
|
174
|
+
activeFileId,
|
|
175
|
+
theme,
|
|
176
|
+
isSettingsOpen,
|
|
177
|
+
editorWantsFocus,
|
|
178
|
+
username,
|
|
179
|
+
} = state;
|
|
155
180
|
|
|
156
181
|
// Functions for dispatching actions to the reducer.
|
|
157
182
|
const {
|
|
@@ -161,6 +186,8 @@ function App() {
|
|
|
161
186
|
setTheme,
|
|
162
187
|
setIsSettingsOpen,
|
|
163
188
|
closeSettings,
|
|
189
|
+
editorNoLongerWantsFocus,
|
|
190
|
+
setUsername,
|
|
164
191
|
} = useActions(dispatch);
|
|
165
192
|
|
|
166
193
|
// The set of open directories.
|
|
@@ -168,8 +195,10 @@ function App() {
|
|
|
168
195
|
const { isDirectoryOpen, toggleDirectory } =
|
|
169
196
|
useOpenDirectories();
|
|
170
197
|
|
|
198
|
+
usePersistUsername(username);
|
|
199
|
+
|
|
171
200
|
// Cache of CodeMirror editors by file id.
|
|
172
|
-
const editorCache = useEditorCache();
|
|
201
|
+
const editorCache: EditorCache = useEditorCache();
|
|
173
202
|
|
|
174
203
|
// Handle dynamic theme changes.
|
|
175
204
|
useDynamicTheme(editorCache, theme);
|
|
@@ -177,9 +206,14 @@ function App() {
|
|
|
177
206
|
// Handle file CRUD operations.
|
|
178
207
|
const {
|
|
179
208
|
createFile,
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
209
|
+
renameFile,
|
|
210
|
+
deleteFile,
|
|
211
|
+
deleteDirectory,
|
|
212
|
+
} = useFileCRUD({
|
|
213
|
+
submitOperation,
|
|
214
|
+
closeTabs,
|
|
215
|
+
openTab,
|
|
216
|
+
});
|
|
183
217
|
|
|
184
218
|
// Isolate the files object from the document.
|
|
185
219
|
const files: Files | null = content
|
|
@@ -191,11 +225,12 @@ function App() {
|
|
|
191
225
|
<div className="app">
|
|
192
226
|
<div className="left">
|
|
193
227
|
<VZSidebar
|
|
194
|
-
createFile={createFile}
|
|
195
228
|
files={files}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
229
|
+
createFile={createFile}
|
|
230
|
+
renameFile={renameFile}
|
|
231
|
+
deleteFile={deleteFile}
|
|
232
|
+
deleteDirectory={deleteDirectory}
|
|
233
|
+
openTab={openTab}
|
|
199
234
|
setIsSettingsOpen={setIsSettingsOpen}
|
|
200
235
|
isDirectoryOpen={isDirectoryOpen}
|
|
201
236
|
toggleDirectory={toggleDirectory}
|
|
@@ -206,6 +241,8 @@ function App() {
|
|
|
206
241
|
onClose={closeSettings}
|
|
207
242
|
theme={theme}
|
|
208
243
|
setTheme={setTheme}
|
|
244
|
+
username={username}
|
|
245
|
+
setUsername={setUsername}
|
|
209
246
|
/>
|
|
210
247
|
</div>
|
|
211
248
|
<div className="right">
|
|
@@ -214,7 +251,9 @@ function App() {
|
|
|
214
251
|
tabList={tabList}
|
|
215
252
|
activeFileId={activeFileId}
|
|
216
253
|
setActiveFileId={setActiveFileId}
|
|
254
|
+
openTab={openTab}
|
|
217
255
|
closeTabs={closeTabs}
|
|
256
|
+
createFile={createFile}
|
|
218
257
|
/>
|
|
219
258
|
{content && activeFileId ? (
|
|
220
259
|
<CodeEditor
|
|
@@ -225,13 +264,17 @@ function App() {
|
|
|
225
264
|
activeFileId={activeFileId}
|
|
226
265
|
theme={theme}
|
|
227
266
|
editorCache={editorCache}
|
|
267
|
+
editorWantsFocus={editorWantsFocus}
|
|
268
|
+
editorNoLongerWantsFocus={
|
|
269
|
+
editorNoLongerWantsFocus
|
|
270
|
+
}
|
|
271
|
+
username={username}
|
|
228
272
|
/>
|
|
229
273
|
) : null}
|
|
230
|
-
<
|
|
231
|
-
prettierError={prettierError}
|
|
232
|
-
/>
|
|
274
|
+
<CodeErrorOverlay errorMessage={prettierError} />
|
|
233
275
|
<PresenceNotifications
|
|
234
276
|
docPresence={docPresence}
|
|
277
|
+
localPresence={localPresence}
|
|
235
278
|
/>
|
|
236
279
|
</div>
|
|
237
280
|
<Resizer />
|
|
@@ -12,6 +12,7 @@ import { json1Presence, textUnicode } from '../../ot';
|
|
|
12
12
|
import {
|
|
13
13
|
FileId,
|
|
14
14
|
ShareDBDoc,
|
|
15
|
+
Username,
|
|
15
16
|
VZCodeContent,
|
|
16
17
|
} from '../../types';
|
|
17
18
|
import { json1PresenceBroadcast } from './json1PresenceBroadcast';
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
EditorCacheValue,
|
|
28
29
|
} from '../useEditorCache';
|
|
29
30
|
import { ThemeLabel, themeOptionsByLabel } from '../themes';
|
|
31
|
+
import { AIAssist } from '../AIAssist';
|
|
30
32
|
|
|
31
33
|
// Language extensions for CodeMirror.
|
|
32
34
|
// Keys are file extensions.
|
|
@@ -64,6 +66,9 @@ export const getOrCreateEditor = ({
|
|
|
64
66
|
theme,
|
|
65
67
|
onInteract,
|
|
66
68
|
editorCache,
|
|
69
|
+
usernameRef,
|
|
70
|
+
aiAssistEndpoint,
|
|
71
|
+
aiAssistOptions,
|
|
67
72
|
}: {
|
|
68
73
|
fileId: FileId;
|
|
69
74
|
|
|
@@ -82,6 +87,11 @@ export const getOrCreateEditor = ({
|
|
|
82
87
|
theme: ThemeLabel;
|
|
83
88
|
onInteract?: () => void;
|
|
84
89
|
editorCache: EditorCache;
|
|
90
|
+
usernameRef: React.MutableRefObject<Username>;
|
|
91
|
+
aiAssistEndpoint?: string;
|
|
92
|
+
aiAssistOptions?: {
|
|
93
|
+
[key: string]: any;
|
|
94
|
+
};
|
|
85
95
|
}): EditorCacheValue => {
|
|
86
96
|
// Cache hit
|
|
87
97
|
if (editorCache.has(fileId)) {
|
|
@@ -118,13 +128,15 @@ export const getOrCreateEditor = ({
|
|
|
118
128
|
);
|
|
119
129
|
|
|
120
130
|
// Deals with broadcasting changes in cursor location and selection.
|
|
121
|
-
if (localPresence)
|
|
131
|
+
if (localPresence) {
|
|
122
132
|
extensions.push(
|
|
123
133
|
json1PresenceBroadcast({
|
|
124
134
|
path: textPath,
|
|
125
135
|
localPresence,
|
|
136
|
+
usernameRef,
|
|
126
137
|
}),
|
|
127
138
|
);
|
|
139
|
+
}
|
|
128
140
|
|
|
129
141
|
// Deals with receiving the broadcas from other clients and displaying them.
|
|
130
142
|
if (docPresence)
|
|
@@ -152,7 +164,7 @@ export const getOrCreateEditor = ({
|
|
|
152
164
|
// using a Compartment.
|
|
153
165
|
const languageCompartment = new Compartment();
|
|
154
166
|
// See https://github.com/vizhub-core/vzcode/issues/55
|
|
155
|
-
const getLanguageExtension = (fileExtension) => {
|
|
167
|
+
const getLanguageExtension = (fileExtension: string) => {
|
|
156
168
|
const languageFunc = languageExtensions[fileExtension];
|
|
157
169
|
return languageFunc ? languageFunc() : undefined;
|
|
158
170
|
};
|
|
@@ -195,6 +207,14 @@ export const getOrCreateEditor = ({
|
|
|
195
207
|
|
|
196
208
|
extensions.push(rotationIndicator);
|
|
197
209
|
|
|
210
|
+
extensions.push(
|
|
211
|
+
AIAssist({
|
|
212
|
+
fileId,
|
|
213
|
+
aiAssistEndpoint,
|
|
214
|
+
aiAssistOptions,
|
|
215
|
+
}),
|
|
216
|
+
);
|
|
217
|
+
|
|
198
218
|
const editor = new EditorView({
|
|
199
219
|
state: EditorState.create({
|
|
200
220
|
doc: text,
|
|
@@ -2,10 +2,12 @@ import {
|
|
|
2
2
|
useRef,
|
|
3
3
|
useLayoutEffect,
|
|
4
4
|
useCallback,
|
|
5
|
+
useMemo,
|
|
5
6
|
} from 'react';
|
|
6
7
|
import {
|
|
7
8
|
FileId,
|
|
8
9
|
ShareDBDoc,
|
|
10
|
+
Username,
|
|
9
11
|
VZCodeContent,
|
|
10
12
|
} from '../../types';
|
|
11
13
|
import { ThemeLabel, defaultTheme } from '../themes';
|
|
@@ -25,6 +27,11 @@ export const CodeEditor = ({
|
|
|
25
27
|
theme = defaultTheme,
|
|
26
28
|
filesPath = ['files'],
|
|
27
29
|
editorCache,
|
|
30
|
+
editorWantsFocus,
|
|
31
|
+
editorNoLongerWantsFocus,
|
|
32
|
+
username,
|
|
33
|
+
aiAssistEndpoint,
|
|
34
|
+
aiAssistOptions,
|
|
28
35
|
}: {
|
|
29
36
|
activeFileId: FileId;
|
|
30
37
|
shareDBDoc: ShareDBDoc<VZCodeContent> | null;
|
|
@@ -39,6 +46,22 @@ export const CodeEditor = ({
|
|
|
39
46
|
// Defaults to `files` if not provided.
|
|
40
47
|
filesPath?: string[];
|
|
41
48
|
editorCache: EditorCache;
|
|
49
|
+
|
|
50
|
+
// Whether the editor should be focused.
|
|
51
|
+
editorWantsFocus: boolean;
|
|
52
|
+
|
|
53
|
+
// Signals that the editor no longer wants focus.
|
|
54
|
+
editorNoLongerWantsFocus: () => void;
|
|
55
|
+
username: Username;
|
|
56
|
+
|
|
57
|
+
// The server endpoint for the AI Assist service.
|
|
58
|
+
aiAssistEndpoint?: string;
|
|
59
|
+
|
|
60
|
+
// Additional options to pass to the AI Assist service,
|
|
61
|
+
// an object with string values.
|
|
62
|
+
aiAssistOptions?: {
|
|
63
|
+
[key: string]: string;
|
|
64
|
+
};
|
|
42
65
|
}) => {
|
|
43
66
|
const ref = useRef<HTMLDivElement>(null);
|
|
44
67
|
|
|
@@ -67,17 +90,15 @@ export const CodeEditor = ({
|
|
|
67
90
|
}, 800);
|
|
68
91
|
}, [submitOperation]);
|
|
69
92
|
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// Guard against cases where page is still loading.
|
|
75
|
-
if (!ref.current) return;
|
|
76
|
-
if (!shareDBDoc) return;
|
|
93
|
+
// Track username on a ref, so that we can use it in the
|
|
94
|
+
// presence object's `username` property.
|
|
95
|
+
const usernameRef = useRef<Username>(username);
|
|
96
|
+
usernameRef.current = username;
|
|
77
97
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
98
|
+
// Get the editor corresponding to the active file.
|
|
99
|
+
// Looks in `editorCache` first, and if not found, creates a new editor.
|
|
100
|
+
const editorCacheValue: EditorCacheValue = useMemo(
|
|
101
|
+
() =>
|
|
81
102
|
getOrCreateEditor({
|
|
82
103
|
fileId: activeFileId,
|
|
83
104
|
shareDBDoc,
|
|
@@ -87,7 +108,32 @@ export const CodeEditor = ({
|
|
|
87
108
|
theme,
|
|
88
109
|
onInteract,
|
|
89
110
|
editorCache,
|
|
90
|
-
|
|
111
|
+
usernameRef,
|
|
112
|
+
aiAssistEndpoint,
|
|
113
|
+
aiAssistOptions,
|
|
114
|
+
}),
|
|
115
|
+
[
|
|
116
|
+
activeFileId,
|
|
117
|
+
shareDBDoc,
|
|
118
|
+
filesPath,
|
|
119
|
+
localPresence,
|
|
120
|
+
docPresence,
|
|
121
|
+
theme,
|
|
122
|
+
onInteract,
|
|
123
|
+
editorCache,
|
|
124
|
+
usernameRef,
|
|
125
|
+
aiAssistEndpoint,
|
|
126
|
+
aiAssistOptions,
|
|
127
|
+
],
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
// Every time the active file switches from one file to another,
|
|
131
|
+
// the editor corresponding to the old file is removed from the DOM,
|
|
132
|
+
// and the editor corresponding to the new file is added to the DOM.
|
|
133
|
+
useLayoutEffect(() => {
|
|
134
|
+
// Guard against cases where page is still loading.
|
|
135
|
+
if (!ref.current) return;
|
|
136
|
+
if (!shareDBDoc) return;
|
|
91
137
|
|
|
92
138
|
// Add the editor to the DOM.
|
|
93
139
|
ref.current.appendChild(editorCacheValue.editor.dom);
|
|
@@ -97,7 +143,24 @@ export const CodeEditor = ({
|
|
|
97
143
|
// This happens every time `activeFileId` changes.
|
|
98
144
|
ref.current.removeChild(editorCacheValue.editor.dom);
|
|
99
145
|
};
|
|
100
|
-
}, [shareDBDoc,
|
|
146
|
+
}, [shareDBDoc, editorCacheValue]);
|
|
147
|
+
|
|
148
|
+
// useEffect(() => {
|
|
149
|
+
// if (editorWantsFocus) {
|
|
150
|
+
// editorCacheValue.editor.focus();
|
|
151
|
+
// }
|
|
152
|
+
|
|
153
|
+
// Focus the editor
|
|
154
|
+
useLayoutEffect(() => {
|
|
155
|
+
if (editorWantsFocus) {
|
|
156
|
+
editorCacheValue.editor.focus();
|
|
157
|
+
editorNoLongerWantsFocus();
|
|
158
|
+
}
|
|
159
|
+
}, [
|
|
160
|
+
editorWantsFocus,
|
|
161
|
+
editorCacheValue,
|
|
162
|
+
editorNoLongerWantsFocus,
|
|
163
|
+
]);
|
|
101
164
|
|
|
102
165
|
return <div className="vz-code-editor" ref={ref}></div>;
|
|
103
166
|
};
|
|
@@ -5,11 +5,13 @@ import { EditorView } from 'codemirror';
|
|
|
5
5
|
export const json1PresenceBroadcast = ({
|
|
6
6
|
path,
|
|
7
7
|
localPresence,
|
|
8
|
+
usernameRef,
|
|
8
9
|
}) =>
|
|
9
10
|
// See https://discuss.codemirror.net/t/codemirror-6-proper-way-to-listen-for-changes/2395/10
|
|
10
11
|
EditorView.updateListener.of((viewUpdate: ViewUpdate) => {
|
|
11
12
|
// If this update modified the cursor / selection,
|
|
12
13
|
// we broadcast the selection update via ShareDB presence.
|
|
14
|
+
|
|
13
15
|
if (viewUpdate.selectionSet) {
|
|
14
16
|
// Isolate the single selection to use for presence.
|
|
15
17
|
// Unfortunately JSON1 with presence does not yet
|
|
@@ -24,8 +26,10 @@ export const json1PresenceBroadcast = ({
|
|
|
24
26
|
start: [...path, from],
|
|
25
27
|
end: [...path, to],
|
|
26
28
|
|
|
27
|
-
//
|
|
28
|
-
username
|
|
29
|
+
// We use a ref here so that the presence always broadcasts
|
|
30
|
+
// the latest username, in the case that it has been changed
|
|
31
|
+
// in the Settings mid-session.
|
|
32
|
+
username: usernameRef.current,
|
|
29
33
|
};
|
|
30
34
|
|
|
31
35
|
// Broadcast presence to remote clients!
|
|
@@ -37,10 +37,6 @@ export const json1PresenceDisplay = ({
|
|
|
37
37
|
// Receive remote presence changes.
|
|
38
38
|
docPresence.on('receive', (id, presence) => {
|
|
39
39
|
// If presence === null, the user has disconnected / exited
|
|
40
|
-
// notification(presence);
|
|
41
|
-
// console.log('docPresence in presenceDisplay:',docPresence);
|
|
42
|
-
// console.log('presence in presenceDisplay:', presence);
|
|
43
|
-
|
|
44
40
|
// We also check if the presence is for the current file or not.
|
|
45
41
|
if (presence && pathMatches(path, presence)) {
|
|
46
42
|
presenceState[id] = presence;
|
|
@@ -107,47 +103,6 @@ export const json1PresenceDisplay = ({
|
|
|
107
103
|
presenceTheme,
|
|
108
104
|
];
|
|
109
105
|
|
|
110
|
-
// const notification = (presence) => {
|
|
111
|
-
// const notif = document.createElement('div');
|
|
112
|
-
// notif.className = 'notification-presence';
|
|
113
|
-
// if (presence === null){
|
|
114
|
-
// notif.textContent = 'User asdfgasdfgasdfg left the collaboration session.';
|
|
115
|
-
// notif.style.display = 'block';
|
|
116
|
-
// }
|
|
117
|
-
// // The following causes the popup to show every time the remote cursor moves
|
|
118
|
-
// // to a different location on the file and not when the user first joins
|
|
119
|
-
// // else {
|
|
120
|
-
// // notif.textContent = presence + 'joined the collaboration session.';
|
|
121
|
-
// // notif.style.display = 'block';
|
|
122
|
-
// // }
|
|
123
|
-
// document.body.appendChild(notif);
|
|
124
|
-
|
|
125
|
-
// const notifStyle = document.createElement('style');
|
|
126
|
-
// notifStyle.innerText = `
|
|
127
|
-
// .notification-presence {
|
|
128
|
-
// position: fixed;
|
|
129
|
-
// display: none;
|
|
130
|
-
// bottom: 20px;
|
|
131
|
-
// right: 20px;
|
|
132
|
-
// max-width: 300px;
|
|
133
|
-
// overflow-x: hidden;
|
|
134
|
-
// white-space: nowrap;
|
|
135
|
-
// background-color: rgb(54, 65, 89);
|
|
136
|
-
// color: rgb(154, 160, 172);
|
|
137
|
-
// padding: 15px;
|
|
138
|
-
// }
|
|
139
|
-
// .notification-presence:hover {
|
|
140
|
-
// overflow-x: auto;
|
|
141
|
-
// }`;
|
|
142
|
-
// document.head.appendChild(notifStyle);
|
|
143
|
-
|
|
144
|
-
// setTimeout(() => {
|
|
145
|
-
// notif.style.display = 'none';
|
|
146
|
-
// }, 10000);
|
|
147
|
-
|
|
148
|
-
// return notif;
|
|
149
|
-
// };
|
|
150
|
-
|
|
151
106
|
const presenceAnnotation = Annotation.define();
|
|
152
107
|
|
|
153
108
|
// Checks that the path of this file
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
2
|
+
import './style.scss';
|
|
3
|
+
|
|
4
|
+
const enableErrorDismiss = true;
|
|
5
|
+
|
|
6
|
+
export const CodeErrorOverlay = ({
|
|
7
|
+
errorMessage,
|
|
8
|
+
}: {
|
|
9
|
+
errorMessage: string | null;
|
|
10
|
+
}) => {
|
|
11
|
+
const [isOverlayVisible, setIsOverlayVisible] =
|
|
12
|
+
useState(true);
|
|
13
|
+
|
|
14
|
+
// If errorMessage changes, set the overlay to be visible
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (errorMessage !== null) {
|
|
17
|
+
setIsOverlayVisible(true);
|
|
18
|
+
}
|
|
19
|
+
}, [errorMessage]);
|
|
20
|
+
|
|
21
|
+
const handleCloseClick = useCallback(() => {
|
|
22
|
+
// Set the visibility state to false when the button is clicked
|
|
23
|
+
setIsOverlayVisible(false);
|
|
24
|
+
}, []);
|
|
25
|
+
|
|
26
|
+
return isOverlayVisible && errorMessage !== null ? (
|
|
27
|
+
<div className="vz-code-error-overlay">
|
|
28
|
+
{errorMessage}
|
|
29
|
+
{enableErrorDismiss ? (
|
|
30
|
+
<div
|
|
31
|
+
className={'bx bx-x error-dismiss-button'}
|
|
32
|
+
onClick={handleCloseClick}
|
|
33
|
+
></div>
|
|
34
|
+
) : null}
|
|
35
|
+
</div>
|
|
36
|
+
) : null;
|
|
37
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
.vz-code-error-overlay {
|
|
2
|
+
color: rgb(255, 35, 35);
|
|
3
|
+
font-family: var(--vzcode-font-family);
|
|
4
|
+
font-size: var(--vzcode-font-size);
|
|
5
|
+
background-color: rgba(0, 0, 0, 0.6);
|
|
6
|
+
position: absolute;
|
|
7
|
+
bottom: 0;
|
|
8
|
+
left: 0;
|
|
9
|
+
right: 0;
|
|
10
|
+
// pointer-events: none;
|
|
11
|
+
margin: 0;
|
|
12
|
+
padding: 8px;
|
|
13
|
+
border-top-left-radius: 12px;
|
|
14
|
+
border-top-right-radius: 12px;
|
|
15
|
+
|
|
16
|
+
/* Define your custom styles here */
|
|
17
|
+
/* For example, change the font size and color */
|
|
18
|
+
font-size: 16px;
|
|
19
|
+
color: #FF5733;
|
|
20
|
+
|
|
21
|
+
/* Add a box shadow for a subtle effect */
|
|
22
|
+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
|
|
23
|
+
|
|
24
|
+
/* Customize the dismiss button style */
|
|
25
|
+
.error-dismiss-button {
|
|
26
|
+
position: absolute;
|
|
27
|
+
top: 5px; /* Adjust the top position */
|
|
28
|
+
right: 5px; /* Adjust the right position */
|
|
29
|
+
margin: 0;
|
|
30
|
+
padding: 5px; /* Adjust padding for the button */
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
color: white;
|
|
33
|
+
background-color: #FF5733; /* Change the button's background color */
|
|
34
|
+
border-radius: 50%; /* Make it circular */
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* Style the button on hover */
|
|
38
|
+
.error-dismiss-button:hover {
|
|
39
|
+
background-color: #FF826D; /* Change the background color on hover */
|
|
40
|
+
}
|
|
41
|
+
}
|