vzcode 1.6.0 → 1.8.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/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-B2OgpmfL.js"></script>
24
- <link rel="stylesheet" crossorigin href="/assets/index-DAYbg783.css">
23
+ <script type="module" crossorigin src="/assets/index-CU0jqXHm.js"></script>
24
+ <link rel="stylesheet" crossorigin href="/assets/index-BKRpkJER.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.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "Multiplayer code editor system",
5
5
  "main": "src/index.ts",
6
6
  "type": "module",
@@ -92,6 +92,7 @@ export const getOrCreateEditor = ({
92
92
  typeScriptWorker,
93
93
  customInteractRules,
94
94
  allowGlobals,
95
+ enableAutoFollowRef,
95
96
  }: {
96
97
  fileId: FileId;
97
98
 
@@ -121,6 +122,10 @@ export const getOrCreateEditor = ({
121
122
  typeScriptWorker: Worker;
122
123
  customInteractRules?: Array<InteractRule>;
123
124
  allowGlobals: boolean;
125
+
126
+ // Ref to a boolean that determines whether to
127
+ // enable auto-following the cursors of remote users.
128
+ enableAutoFollowRef: React.MutableRefObject<boolean>;
124
129
  }): EditorCacheValue => {
125
130
  // Cache hit
126
131
  if (editorCache.has(fileId)) {
@@ -173,6 +178,7 @@ export const getOrCreateEditor = ({
173
178
  json1PresenceDisplay({
174
179
  path: textPath,
175
180
  docPresence,
181
+ enableAutoFollowRef,
176
182
  }),
177
183
  );
178
184
  }
@@ -4,6 +4,7 @@ import {
4
4
  useCallback,
5
5
  useMemo,
6
6
  useContext,
7
+ useEffect,
7
8
  } from 'react';
8
9
  import { Username } from '../../types';
9
10
  import { EditorCacheValue } from '../useEditorCache';
@@ -36,10 +37,9 @@ export const CodeEditor = ({
36
37
  typeScriptWorker,
37
38
  theme,
38
39
  codeEditorRef,
40
+ enableAutoFollow,
39
41
  } = useContext(VZCodeContext);
40
42
 
41
- const ref = codeEditorRef;
42
-
43
43
  // Set `doc.data.isInteracting` to `true` when the user is interacting
44
44
  // via interactive code widgets (e.g. Alt+drag), and `false` when they are not.
45
45
  const interactTimeoutRef = useRef(null);
@@ -71,6 +71,13 @@ export const CodeEditor = ({
71
71
  const usernameRef = useRef<Username>(username);
72
72
  usernameRef.current = username;
73
73
 
74
+ // Track enableAutoFollow on a ref, so that we can use it in the
75
+ // CodeMirror extension.
76
+ const enableAutoFollowRef = useRef(enableAutoFollow);
77
+ useEffect(() => {
78
+ enableAutoFollowRef.current = enableAutoFollow;
79
+ }, [enableAutoFollow]);
80
+
74
81
  // Get the editor corresponding to the active file.
75
82
  // Looks in `editorCache` first, and if not found, creates a new editor.
76
83
  const editorCacheValue: EditorCacheValue = useMemo(
@@ -89,6 +96,7 @@ export const CodeEditor = ({
89
96
  typeScriptWorker,
90
97
  customInteractRules,
91
98
  allowGlobals,
99
+ enableAutoFollowRef,
92
100
  }),
93
101
  [
94
102
  activeFileId,
@@ -109,11 +117,13 @@ export const CodeEditor = ({
109
117
  // and the editor corresponding to the new file is added to the DOM.
110
118
  useLayoutEffect(() => {
111
119
  // Guard against cases where page is still loading.
112
- if (!ref.current) return;
120
+ if (!codeEditorRef.current) return;
113
121
  if (!content) return;
114
122
 
115
123
  // Add the editor and apply the prior scroll position to the DOM.
116
- ref.current.appendChild(editorCacheValue.editor.dom);
124
+ codeEditorRef.current.appendChild(
125
+ editorCacheValue.editor.dom,
126
+ );
117
127
  editorCacheValue.editor.scrollDOM.scrollTo({
118
128
  top: editorCacheValue.scrollPosition ?? 0,
119
129
  });
@@ -123,7 +133,9 @@ export const CodeEditor = ({
123
133
  // This happens every time `activeFileId` changes.
124
134
  editorCacheValue.scrollPosition =
125
135
  editorCacheValue.editor.scrollDOM.scrollTop;
126
- ref.current.removeChild(editorCacheValue.editor.dom);
136
+ codeEditorRef.current.removeChild(
137
+ editorCacheValue.editor.dom,
138
+ );
127
139
  };
128
140
  }, [shareDBDoc, editorCacheValue]);
129
141
 
@@ -142,8 +154,8 @@ export const CodeEditor = ({
142
154
  return (
143
155
  <div
144
156
  className="vz-code-editor"
145
- ref={ref}
157
+ ref={codeEditorRef}
146
158
  tabIndex={-1}
147
- ></div>
159
+ />
148
160
  );
149
161
  };
@@ -10,10 +10,10 @@ import { Username } from '../../types';
10
10
 
11
11
  const debug = false;
12
12
 
13
- // TODO: Make this a setting in the UI.
14
- // See https://github.com/vizhub-core/vzcode/issues/739
15
- const enableAutoFollow = false;
16
-
13
+ // export let enableAutoFollow = false;
14
+ // export const toggleAutoFollowButton = () => {
15
+ // enableAutoFollow = !enableAutoFollow;
16
+ // };
17
17
  // Deals with receiving the broadcasted presence cursor locations
18
18
  // from other clients and displaying them.
19
19
  //
@@ -25,6 +25,7 @@ const enableAutoFollow = false;
25
25
  export const json1PresenceDisplay = ({
26
26
  path,
27
27
  docPresence,
28
+ enableAutoFollowRef,
28
29
  }) => [
29
30
  ViewPlugin.fromClass(
30
31
  class {
@@ -146,7 +147,7 @@ export const json1PresenceDisplay = ({
146
147
 
147
148
  // Auto-follow all users when their presence is broadcast
148
149
  // by scrolling them into view.
149
- if (enableAutoFollow) {
150
+ if (enableAutoFollowRef.current) {
150
151
  this.scrollToCursor(view);
151
152
  }
152
153
  });
@@ -10,6 +10,7 @@ export { FileSVG } from './FileSVG';
10
10
  export { DirectoryArrowSVG } from './DirectoryArrowSVG';
11
11
  export { PlaySVG } from './PlaySVG';
12
12
  export { QuestionMarkSVG } from './QuestionMarkSVG';
13
+ export { PinSVG } from './PinSVG';
13
14
  export { FolderSVG } from './FolderSVG';
14
15
  export { SearchSVG } from './SearchSVG';
15
16
  export { SearchFileSVG } from './SearchFileSVG';
@@ -11,8 +11,8 @@
11
11
  font-weight: 500;
12
12
  align-items: center;
13
13
  display: flex;
14
- gap: 8px;
15
- padding: 6px 6px 6px 16px;
14
+ gap: 6px;
15
+ padding: 4px 4px 4px 14px;
16
16
  border-top-left-radius: 8px;
17
17
  border-top-right-radius: 8px;
18
18
  margin-top: 6px;
@@ -149,6 +149,9 @@ export type VZCodeContextValue = {
149
149
 
150
150
  hoveredItemId: ItemId | null;
151
151
  setHoveredItemId: (itemId: ItemId | null) => void;
152
+
153
+ enableAutoFollow: boolean;
154
+ toggleAutoFollow: () => void;
152
155
  };
153
156
 
154
157
  export const VZCodeProvider = ({
@@ -236,6 +239,7 @@ export const VZCodeProvider = ({
236
239
  isDocOpen,
237
240
  editorWantsFocus,
238
241
  username,
242
+ enableAutoFollow,
239
243
  } = state;
240
244
 
241
245
  // Functions for dispatching actions to the reducer.
@@ -256,6 +260,7 @@ export const VZCodeProvider = ({
256
260
  closeDoc,
257
261
  editorNoLongerWantsFocus,
258
262
  setUsername,
263
+ toggleAutoFollow,
259
264
  } = useActions(dispatch);
260
265
 
261
266
  // Sync tab state to the URL.
@@ -428,6 +433,9 @@ export const VZCodeProvider = ({
428
433
 
429
434
  hoveredItemId,
430
435
  setHoveredItemId,
436
+
437
+ enableAutoFollow,
438
+ toggleAutoFollow,
431
439
  };
432
440
 
433
441
  return (
@@ -1,4 +1,9 @@
1
- import { useCallback, useContext, useMemo } from 'react';
1
+ import {
2
+ useCallback,
3
+ useContext,
4
+ useMemo,
5
+ useState,
6
+ } from 'react';
2
7
  import {
3
8
  FileId,
4
9
  FileTree,
@@ -17,6 +22,7 @@ import {
17
22
  NewSVG,
18
23
  FileSVG,
19
24
  QuestionMarkSVG,
25
+ PinSVG,
20
26
  } from '../Icons';
21
27
  import { VZCodeContext } from '../VZCodeContext';
22
28
  import { Listing } from './Listing';
@@ -56,6 +62,8 @@ export const VZSidebar = ({
56
62
  handleOpenCreateDirModal,
57
63
  connected,
58
64
  sidebarRef,
65
+ enableAutoFollow,
66
+ toggleAutoFollow,
59
67
  } = useContext(VZCodeContext);
60
68
 
61
69
  const fileTree = useMemo(
@@ -233,6 +241,29 @@ export const VZSidebar = ({
233
241
  <FileSVG />
234
242
  </i>
235
243
  </OverlayTrigger>
244
+
245
+ {/*Toggle Follow*/}
246
+ <OverlayTrigger
247
+ placement="right"
248
+ overlay={
249
+ <Tooltip id="toggle-auto-follow">
250
+ {enableAutoFollow
251
+ ? 'Disable Auto Follow'
252
+ : 'Enable Auto Follow'}
253
+ </Tooltip>
254
+ }
255
+ >
256
+ <i
257
+ onClick={toggleAutoFollow}
258
+ className={`icon-button icon-button-dark${
259
+ enableAutoFollow
260
+ ? ' vh-color-success-01'
261
+ : ''
262
+ }`}
263
+ >
264
+ <PinSVG />
265
+ </i>
266
+ </OverlayTrigger>
236
267
  </div>
237
268
 
238
269
  <div className="files">
@@ -18,6 +18,7 @@
18
18
  }
19
19
 
20
20
  .files {
21
+ margin-top: 6px;
21
22
  display: flex;
22
23
  flex-direction: row;
23
24
  justify-content: start;
@@ -35,9 +36,9 @@
35
36
  align-items: center;
36
37
  justify-content: space-between;
37
38
  cursor: pointer;
38
- margin: 0 8px;
39
- padding: 0 4px 0 6px;
40
- border-radius: 4px;
39
+ margin: 0 6px;
40
+ padding: 0 4px 0 8px;
41
+ border-radius: 8px;
41
42
  font-family: var(--vzcode-font-family);
42
43
  font-weight: 500;
43
44
  }
@@ -59,7 +60,7 @@
59
60
  gap: 6px;
60
61
  padding: 6px;
61
62
 
62
- * {
63
+ a {
63
64
  color: inherit;
64
65
  }
65
66
  }
@@ -158,6 +158,12 @@ export const useActions = (
158
158
  [dispatch],
159
159
  );
160
160
 
161
+ const toggleAutoFollow = useCallback(() => {
162
+ dispatch({
163
+ type: 'toggle_auto_follow',
164
+ });
165
+ }, [dispatch]);
166
+
161
167
  return {
162
168
  setActiveFileId,
163
169
  setActiveFileLeft,
@@ -175,5 +181,6 @@ export const useActions = (
175
181
  closeDoc,
176
182
  editorNoLongerWantsFocus,
177
183
  setUsername,
184
+ toggleAutoFollow,
178
185
  };
179
186
  };
@@ -1,7 +1,8 @@
1
1
  import ts from 'typescript';
2
2
 
3
3
  export const compilerOptions: ts.CompilerOptions = {
4
- lib: ['dom', 'esnext'],
4
+ target: ts.ScriptTarget.ES2022,
5
+ lib: ['dom', 'es2022'],
5
6
 
6
7
  // Disable warnings around "Any type".
7
8
  noImplicitAny: false,
@@ -25,6 +25,7 @@ import {
25
25
  setSearchResultsReducer,
26
26
  setSearchFileVisibilityReducer,
27
27
  } from './searchReducer';
28
+ import { toggleAutoFollowReducer } from './toggleAutoFollowReducer';
28
29
  export { createInitialState } from './createInitialState';
29
30
 
30
31
  // The shape of the state managed by the reducer.
@@ -55,6 +56,11 @@ export type VZState = {
55
56
 
56
57
  // The username of the current user.
57
58
  username: Username;
59
+
60
+ // True if "auto-follow" is enabled.
61
+ // This is a feature that automatically scrolls the editor to the
62
+ // currently active remote user's cursor position.
63
+ enableAutoFollow: boolean;
58
64
  };
59
65
 
60
66
  // The shape of the actions that can be dispatched to the reducer.
@@ -121,7 +127,11 @@ export type VZAction =
121
127
 
122
128
  // `set_username`
123
129
  // * Sets the username.
124
- | { type: 'set_username'; username: Username };
130
+ | { type: 'set_username'; username: Username }
131
+
132
+ // `toggle_auto_follow
133
+ // * Toggles the auto-follow feature.
134
+ | { type: 'toggle_auto_follow' };
125
135
 
126
136
  // Representation of an open tab.
127
137
  export type TabState = {
@@ -157,6 +167,7 @@ const reducers = [
157
167
  setIsDocOpenReducer,
158
168
  editorNoLongerWantsFocusReducer,
159
169
  setUsernameReducer,
170
+ toggleAutoFollowReducer,
160
171
  ];
161
172
 
162
173
  export const vzReducer = (
@@ -0,0 +1,12 @@
1
+ import { VZAction, VZState } from '.';
2
+
3
+ export const toggleAutoFollowReducer = (
4
+ state: VZState,
5
+ action: VZAction,
6
+ ): VZState =>
7
+ action.type === 'toggle_auto_follow'
8
+ ? {
9
+ ...state,
10
+ enableAutoFollow: !state.enableAutoFollow,
11
+ }
12
+ : state;