vzcode 0.26.0 → 0.27.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-5f9f376e.css +1 -0
- package/dist/assets/index-70b6d947.js +111 -0
- package/dist/index.html +2 -2
- package/package.json +4 -4
- package/src/client/App.tsx +86 -43
- package/src/client/CodeEditor/getOrCreateEditor.ts +6 -1
- package/src/client/CodeEditor/index.tsx +60 -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 +27 -0
- package/src/client/CodeErrorOverlay/style.scss +30 -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 +43 -24
- 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/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/dist/index.html
CHANGED
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
href="https://unpkg.com/boxicons@2.0.7/css/boxicons.min.css"
|
|
25
25
|
rel="stylesheet"
|
|
26
26
|
/>
|
|
27
|
-
<script type="module" crossorigin src="/assets/index-
|
|
28
|
-
<link rel="stylesheet" href="/assets/index-
|
|
27
|
+
<script type="module" crossorigin src="/assets/index-70b6d947.js"></script>
|
|
28
|
+
<link rel="stylesheet" href="/assets/index-5f9f376e.css">
|
|
29
29
|
</head>
|
|
30
30
|
<body>
|
|
31
31
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vzcode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Multiplayer code editor system",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@codemirror/lang-markdown": "^6.2.2",
|
|
52
52
|
"@codemirror/state": "^6.3.1",
|
|
53
53
|
"@codemirror/theme-one-dark": "^6.1.2",
|
|
54
|
-
"@codemirror/view": "^6.21.
|
|
54
|
+
"@codemirror/view": "^6.21.4",
|
|
55
55
|
"@lezer/highlight": "^1.1.6",
|
|
56
56
|
"@replit/codemirror-interact": "^6.3.0",
|
|
57
57
|
"@teamwork/websocket-json-stream": "^2.0.0",
|
|
@@ -83,11 +83,11 @@
|
|
|
83
83
|
"ws": "^8.14.2"
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
|
-
"@types/react": "^18.2.
|
|
86
|
+
"@types/react": "^18.2.33",
|
|
87
87
|
"@types/react-dom": "^18.2.14",
|
|
88
88
|
"@vitejs/plugin-react": "^4.1.0",
|
|
89
89
|
"prettier": "^3.0.3",
|
|
90
|
-
"sass": "^1.69.
|
|
90
|
+
"sass": "^1.69.5",
|
|
91
91
|
"typescript": "^5.2.2",
|
|
92
92
|
"vite": "^4.5.0",
|
|
93
93
|
"vitest": "^0.34.6"
|
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';
|
|
@@ -64,6 +65,7 @@ export const getOrCreateEditor = ({
|
|
|
64
65
|
theme,
|
|
65
66
|
onInteract,
|
|
66
67
|
editorCache,
|
|
68
|
+
usernameRef,
|
|
67
69
|
}: {
|
|
68
70
|
fileId: FileId;
|
|
69
71
|
|
|
@@ -82,6 +84,7 @@ export const getOrCreateEditor = ({
|
|
|
82
84
|
theme: ThemeLabel;
|
|
83
85
|
onInteract?: () => void;
|
|
84
86
|
editorCache: EditorCache;
|
|
87
|
+
usernameRef: React.MutableRefObject<Username>;
|
|
85
88
|
}): EditorCacheValue => {
|
|
86
89
|
// Cache hit
|
|
87
90
|
if (editorCache.has(fileId)) {
|
|
@@ -118,13 +121,15 @@ export const getOrCreateEditor = ({
|
|
|
118
121
|
);
|
|
119
122
|
|
|
120
123
|
// Deals with broadcasting changes in cursor location and selection.
|
|
121
|
-
if (localPresence)
|
|
124
|
+
if (localPresence) {
|
|
122
125
|
extensions.push(
|
|
123
126
|
json1PresenceBroadcast({
|
|
124
127
|
path: textPath,
|
|
125
128
|
localPresence,
|
|
129
|
+
usernameRef,
|
|
126
130
|
}),
|
|
127
131
|
);
|
|
132
|
+
}
|
|
128
133
|
|
|
129
134
|
// Deals with receiving the broadcas from other clients and displaying them.
|
|
130
135
|
if (docPresence)
|
|
@@ -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,9 @@ export const CodeEditor = ({
|
|
|
25
27
|
theme = defaultTheme,
|
|
26
28
|
filesPath = ['files'],
|
|
27
29
|
editorCache,
|
|
30
|
+
editorWantsFocus,
|
|
31
|
+
editorNoLongerWantsFocus,
|
|
32
|
+
username,
|
|
28
33
|
}: {
|
|
29
34
|
activeFileId: FileId;
|
|
30
35
|
shareDBDoc: ShareDBDoc<VZCodeContent> | null;
|
|
@@ -39,6 +44,13 @@ export const CodeEditor = ({
|
|
|
39
44
|
// Defaults to `files` if not provided.
|
|
40
45
|
filesPath?: string[];
|
|
41
46
|
editorCache: EditorCache;
|
|
47
|
+
|
|
48
|
+
// Whether the editor should be focused.
|
|
49
|
+
editorWantsFocus: boolean;
|
|
50
|
+
|
|
51
|
+
// Signals that the editor no longer wants focus.
|
|
52
|
+
editorNoLongerWantsFocus: () => void;
|
|
53
|
+
username: Username;
|
|
42
54
|
}) => {
|
|
43
55
|
const ref = useRef<HTMLDivElement>(null);
|
|
44
56
|
|
|
@@ -67,17 +79,15 @@ export const CodeEditor = ({
|
|
|
67
79
|
}, 800);
|
|
68
80
|
}, [submitOperation]);
|
|
69
81
|
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// Guard against cases where page is still loading.
|
|
75
|
-
if (!ref.current) return;
|
|
76
|
-
if (!shareDBDoc) return;
|
|
82
|
+
// Track username on a ref, so that we can use it in the
|
|
83
|
+
// presence object's `username` property.
|
|
84
|
+
const usernameRef = useRef<Username>(username);
|
|
85
|
+
usernameRef.current = username;
|
|
77
86
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
87
|
+
// Get the editor corresponding to the active file.
|
|
88
|
+
// Looks in `editorCache` first, and if not found, creates a new editor.
|
|
89
|
+
const editorCacheValue: EditorCacheValue = useMemo(
|
|
90
|
+
() =>
|
|
81
91
|
getOrCreateEditor({
|
|
82
92
|
fileId: activeFileId,
|
|
83
93
|
shareDBDoc,
|
|
@@ -87,7 +97,28 @@ export const CodeEditor = ({
|
|
|
87
97
|
theme,
|
|
88
98
|
onInteract,
|
|
89
99
|
editorCache,
|
|
90
|
-
|
|
100
|
+
usernameRef,
|
|
101
|
+
}),
|
|
102
|
+
[
|
|
103
|
+
activeFileId,
|
|
104
|
+
shareDBDoc,
|
|
105
|
+
filesPath,
|
|
106
|
+
localPresence,
|
|
107
|
+
docPresence,
|
|
108
|
+
theme,
|
|
109
|
+
onInteract,
|
|
110
|
+
editorCache,
|
|
111
|
+
usernameRef,
|
|
112
|
+
],
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// Every time the active file switches from one file to another,
|
|
116
|
+
// the editor corresponding to the old file is removed from the DOM,
|
|
117
|
+
// and the editor corresponding to the new file is added to the DOM.
|
|
118
|
+
useLayoutEffect(() => {
|
|
119
|
+
// Guard against cases where page is still loading.
|
|
120
|
+
if (!ref.current) return;
|
|
121
|
+
if (!shareDBDoc) return;
|
|
91
122
|
|
|
92
123
|
// Add the editor to the DOM.
|
|
93
124
|
ref.current.appendChild(editorCacheValue.editor.dom);
|
|
@@ -97,7 +128,24 @@ export const CodeEditor = ({
|
|
|
97
128
|
// This happens every time `activeFileId` changes.
|
|
98
129
|
ref.current.removeChild(editorCacheValue.editor.dom);
|
|
99
130
|
};
|
|
100
|
-
}, [shareDBDoc,
|
|
131
|
+
}, [shareDBDoc, editorCacheValue]);
|
|
132
|
+
|
|
133
|
+
// useEffect(() => {
|
|
134
|
+
// if (editorWantsFocus) {
|
|
135
|
+
// editorCacheValue.editor.focus();
|
|
136
|
+
// }
|
|
137
|
+
|
|
138
|
+
// Focus the editor
|
|
139
|
+
useLayoutEffect(() => {
|
|
140
|
+
if (editorWantsFocus) {
|
|
141
|
+
editorCacheValue.editor.focus();
|
|
142
|
+
editorNoLongerWantsFocus();
|
|
143
|
+
}
|
|
144
|
+
}, [
|
|
145
|
+
editorWantsFocus,
|
|
146
|
+
editorCacheValue,
|
|
147
|
+
editorNoLongerWantsFocus,
|
|
148
|
+
]);
|
|
101
149
|
|
|
102
150
|
return <div className="vz-code-editor" ref={ref}></div>;
|
|
103
151
|
};
|
|
@@ -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,27 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import './style.scss';
|
|
3
|
+
|
|
4
|
+
// Feature flag for the button that dismisses the error.
|
|
5
|
+
const enableErrorDismiss = false;
|
|
6
|
+
|
|
7
|
+
export const CodeErrorOverlay = ({
|
|
8
|
+
errorMessage,
|
|
9
|
+
}: {
|
|
10
|
+
errorMessage: string | null;
|
|
11
|
+
}) => {
|
|
12
|
+
const handleCloseClick = useCallback(() => {
|
|
13
|
+
console.log('TODO dismiss the error');
|
|
14
|
+
}, []);
|
|
15
|
+
|
|
16
|
+
return errorMessage !== null ? (
|
|
17
|
+
<pre className="vz-code-error-overlay">
|
|
18
|
+
{errorMessage}
|
|
19
|
+
{enableErrorDismiss ? (
|
|
20
|
+
<div
|
|
21
|
+
className={'bx bx-x error-dismiss-button'}
|
|
22
|
+
onClick={handleCloseClick}
|
|
23
|
+
></div>
|
|
24
|
+
) : null}
|
|
25
|
+
</pre>
|
|
26
|
+
) : null;
|
|
27
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
|
|
14
|
+
border-top-left-radius: 12px;
|
|
15
|
+
border-top-right-radius: 12px;
|
|
16
|
+
|
|
17
|
+
// TODO unify with styles in tab close button
|
|
18
|
+
.error-dismiss-button {
|
|
19
|
+
position: absolute;
|
|
20
|
+
top: 0;
|
|
21
|
+
right: 0;
|
|
22
|
+
margin: 5px;
|
|
23
|
+
cursor: pointer;
|
|
24
|
+
color: white;
|
|
25
|
+
}
|
|
26
|
+
.error-dismiss-button:hover {
|
|
27
|
+
border-radius: 50%;
|
|
28
|
+
background-color: rgba(255, 255, 255, 0.2);
|
|
29
|
+
}
|
|
30
|
+
}
|