vzcode 1.11.0 → 1.13.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-Bm65AzMP.js +425 -0
- package/dist/assets/index-BpeGnq7b.js +157 -0
- package/dist/assets/{index-BKRpkJER.css → index-Wxh-Jaj6.css} +1 -1
- package/dist/assets/worker-CJWrLBN2.js +286 -0
- package/dist/index.html +2 -2
- package/package.json +18 -17
- package/src/client/AIAssist/AIAssistCodeMirrorKeyMap.ts +5 -2
- package/src/client/AIAssist/startAIAssist.ts +1 -1
- package/src/client/CodeEditor/getOrCreateEditor.ts +11 -4
- package/src/client/CodeEditor/index.tsx +2 -0
- package/src/client/CodeEditor/json1PresenceDisplay.ts +1 -1
- package/src/client/Icons/SplitEditorSVG.tsx +34 -0
- package/src/client/RunCodeWidget/index.tsx +48 -16
- package/src/client/RunCodeWidget/style.scss +6 -1
- package/src/client/TabList/Tab.tsx +1 -2
- package/src/client/TabList/index.tsx +1 -1
- package/src/client/VZCodeContext.tsx +30 -7
- package/src/client/VZKeyboardShortcutsDoc.tsx +2 -1
- package/src/client/VZSidebar/DirectoryListing.tsx +2 -1
- package/src/client/VZSidebar/FileListing.tsx +23 -3
- package/src/client/VZSidebar/Item.tsx +2 -2
- package/src/client/VZSidebar/Listing.tsx +8 -1
- package/src/client/VZSidebar/Search.tsx +7 -2
- package/src/client/VZSidebar/index.tsx +130 -24
- package/src/client/VZSidebar/styles.scss +25 -0
- package/src/client/tabsSearchParameters.ts +1 -2
- package/src/client/useActions.ts +29 -1
- package/src/client/useKeyboardShortcuts.ts +31 -1
- package/src/client/useURLSync.ts +1 -2
- package/src/client/vzReducer/closeTabReducer.test.ts +49 -33
- package/src/client/vzReducer/closeTabsReducer.ts +51 -19
- package/src/client/vzReducer/createInitialState.ts +9 -3
- package/src/client/vzReducer/findPane.ts +19 -0
- package/src/client/vzReducer/index.ts +38 -27
- package/src/client/vzReducer/openTabReducer.test.ts +38 -21
- package/src/client/vzReducer/openTabReducer.ts +26 -10
- package/src/client/vzReducer/searchReducer.ts +21 -1
- package/src/client/vzReducer/setActiveFileIdReducer.ts +23 -4
- package/src/client/vzReducer/setActiveFileLeftRightReducer.ts +37 -8
- package/src/client/vzReducer/splitCurrentPaneReducer.ts +71 -0
- package/src/client/vzReducer/updatePane.ts +44 -0
- package/src/client/vzReducer/updatePresenceIndicatorReducer.ts +30 -0
- package/src/server/index.js +1 -0
- package/src/types.ts +86 -1
- package/dist/assets/index--bvX81xc.js +0 -425
- package/dist/assets/index-BSuijdti.js +0 -157
- package/dist/assets/worker-Cs0ZU3m1.js +0 -291
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-
|
|
24
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
23
|
+
<script type="module" crossorigin src="/assets/index-BpeGnq7b.js"></script>
|
|
24
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Wxh-Jaj6.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.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "Multiplayer code editor system",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"test-interactive": "cd test/sampleDirectories/kitchenSink && node ../../../src/server/index.js",
|
|
15
15
|
"prettier": "prettier {*.*,**/*.*} --write",
|
|
16
16
|
"tsc": "tsc --noEmit",
|
|
17
|
-
"dev": "
|
|
17
|
+
"dev": "cross-env EDITOR_PORT=5173 concurrently \"npm run test-interactive\" \"vite\"",
|
|
18
18
|
"build": "vite build",
|
|
19
19
|
"build-release": "vite build --mode release",
|
|
20
20
|
"preview": "concurrently \"npm run test-interactive\" \"vite preview\"",
|
|
@@ -111,14 +111,14 @@
|
|
|
111
111
|
"@codemirror/lang-markdown": "^6.2.5",
|
|
112
112
|
"@codemirror/state": "^6.4.1",
|
|
113
113
|
"@codemirror/theme-one-dark": "^6.1.2",
|
|
114
|
-
"@codemirror/view": "^6.
|
|
114
|
+
"@codemirror/view": "^6.29.1",
|
|
115
115
|
"@lezer/highlight": "^1.2.0",
|
|
116
|
-
"@replit/codemirror-indentation-markers": "^6.5.
|
|
116
|
+
"@replit/codemirror-indentation-markers": "^6.5.3",
|
|
117
117
|
"@replit/codemirror-interact": "^6.3.1",
|
|
118
118
|
"@replit/codemirror-lang-svelte": "^6.0.0",
|
|
119
119
|
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
|
120
120
|
"@teamwork/websocket-json-stream": "^2.0.0",
|
|
121
|
-
"@typescript/vfs": "^1.
|
|
121
|
+
"@typescript/vfs": "^1.6.0",
|
|
122
122
|
"@uiw/codemirror-theme-abcdef": "^4.23.0",
|
|
123
123
|
"@uiw/codemirror-theme-dracula": "^4.23.0",
|
|
124
124
|
"@uiw/codemirror-theme-eclipse": "^4.23.0",
|
|
@@ -140,14 +140,14 @@
|
|
|
140
140
|
"json0-ot-diff": "^1.1.2",
|
|
141
141
|
"ngrok": "^4.3.3",
|
|
142
142
|
"open": "^10.1.0",
|
|
143
|
-
"openai": "^4.
|
|
144
|
-
"prettier-plugin-svelte": "^3.2.
|
|
143
|
+
"openai": "^4.53.2",
|
|
144
|
+
"prettier-plugin-svelte": "^3.2.6",
|
|
145
145
|
"react": "^18.3.1",
|
|
146
146
|
"react-bootstrap": "^2.10.4",
|
|
147
147
|
"react-dom": "^18.3.1",
|
|
148
|
-
"react-router-dom": "^6.
|
|
149
|
-
"sharedb": "^5.0.
|
|
150
|
-
"sharedb-client-browser": "^5.0.
|
|
148
|
+
"react-router-dom": "^6.25.1",
|
|
149
|
+
"sharedb": "^5.0.3",
|
|
150
|
+
"sharedb-client-browser": "^5.0.3",
|
|
151
151
|
"vizhub-ui": "^3.23.0",
|
|
152
152
|
"ws": "^8.18.0"
|
|
153
153
|
},
|
|
@@ -156,14 +156,15 @@
|
|
|
156
156
|
"@types/react-dom": "^18.3.0",
|
|
157
157
|
"@vitejs/plugin-react": "^4.3.1",
|
|
158
158
|
"concurrently": "^8.2.2",
|
|
159
|
-
"
|
|
160
|
-
"
|
|
161
|
-
"
|
|
162
|
-
"
|
|
163
|
-
"
|
|
159
|
+
"cross-env": "^7.0.3",
|
|
160
|
+
"prettier": "^3.3.3",
|
|
161
|
+
"sass": "^1.77.8",
|
|
162
|
+
"typescript": "^5.5.4",
|
|
163
|
+
"vite": "^5.3.5",
|
|
164
|
+
"vitest": "^2.0.4"
|
|
164
165
|
},
|
|
165
166
|
"optionalDependencies": {
|
|
166
|
-
"@rollup/rollup-darwin-arm64": "^4.
|
|
167
|
-
"@rollup/rollup-win32-x64-msvc": "^4.
|
|
167
|
+
"@rollup/rollup-darwin-arm64": "^4.19.1",
|
|
168
|
+
"@rollup/rollup-win32-x64-msvc": "^4.19.1"
|
|
168
169
|
}
|
|
169
170
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { EditorView, keymap } from '@codemirror/view';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
ShareDBDoc,
|
|
4
|
+
TabState,
|
|
5
|
+
VZCodeContent,
|
|
6
|
+
} from '../../types';
|
|
4
7
|
|
|
5
8
|
export const AIAssistCodeMirrorKeyMap = ({
|
|
6
9
|
shareDBDoc,
|
|
@@ -18,7 +18,9 @@ import { Diagnostic, linter } from '@codemirror/lint';
|
|
|
18
18
|
import { json1Presence, textUnicode } from '../../ot';
|
|
19
19
|
import {
|
|
20
20
|
FileId,
|
|
21
|
+
PaneId,
|
|
21
22
|
ShareDBDoc,
|
|
23
|
+
TabState,
|
|
22
24
|
Username,
|
|
23
25
|
VZCodeContent,
|
|
24
26
|
} from '../../types';
|
|
@@ -40,7 +42,6 @@ import { keymap } from '@codemirror/view';
|
|
|
40
42
|
import { basicSetup } from './basicSetup';
|
|
41
43
|
import { InteractRule } from '@replit/codemirror-interact';
|
|
42
44
|
import rainbowBrackets from '../CodeEditor/rainbowBrackets';
|
|
43
|
-
import { TabState } from '../vzReducer';
|
|
44
45
|
import { cssLanguage } from '@codemirror/lang-css';
|
|
45
46
|
import { javascriptLanguage } from '@codemirror/lang-javascript';
|
|
46
47
|
|
|
@@ -102,6 +103,7 @@ const getAtPath = (obj, path) => {
|
|
|
102
103
|
// Gets or creates an `editorCache` entry for the given file id.
|
|
103
104
|
// Looks in `editorCache` first, and if not found, creates a new editor.
|
|
104
105
|
export const getOrCreateEditor = ({
|
|
106
|
+
paneId = 'root',
|
|
105
107
|
fileId,
|
|
106
108
|
shareDBDoc,
|
|
107
109
|
content,
|
|
@@ -118,6 +120,8 @@ export const getOrCreateEditor = ({
|
|
|
118
120
|
enableAutoFollowRef,
|
|
119
121
|
openTab,
|
|
120
122
|
}: {
|
|
123
|
+
// TODO pass this in from the outside
|
|
124
|
+
paneId?: PaneId;
|
|
121
125
|
fileId: FileId;
|
|
122
126
|
|
|
123
127
|
// The ShareDB document that contains the file.
|
|
@@ -153,8 +157,11 @@ export const getOrCreateEditor = ({
|
|
|
153
157
|
openTab: (tabState: TabState) => void;
|
|
154
158
|
}): EditorCacheValue => {
|
|
155
159
|
// Cache hit
|
|
156
|
-
|
|
157
|
-
|
|
160
|
+
|
|
161
|
+
const cacheKey = fileId + '|' + paneId;
|
|
162
|
+
|
|
163
|
+
if (editorCache.has(cacheKey)) {
|
|
164
|
+
return editorCache.get(cacheKey);
|
|
158
165
|
}
|
|
159
166
|
|
|
160
167
|
// Cache miss
|
|
@@ -385,7 +392,7 @@ export const getOrCreateEditor = ({
|
|
|
385
392
|
const editorCacheValue = { editor, themeCompartment };
|
|
386
393
|
|
|
387
394
|
// Populate the cache.
|
|
388
|
-
editorCache.set(
|
|
395
|
+
editorCache.set(cacheKey, editorCacheValue);
|
|
389
396
|
|
|
390
397
|
return editorCacheValue;
|
|
391
398
|
};
|
|
@@ -99,6 +99,7 @@ export const CodeEditor = ({
|
|
|
99
99
|
allowGlobals,
|
|
100
100
|
enableAutoFollowRef,
|
|
101
101
|
openTab,
|
|
102
|
+
// splitPaneView,
|
|
102
103
|
}),
|
|
103
104
|
[
|
|
104
105
|
activeFileId,
|
|
@@ -121,6 +122,7 @@ export const CodeEditor = ({
|
|
|
121
122
|
// Guard against cases where page is still loading.
|
|
122
123
|
if (!codeEditorRef.current) return;
|
|
123
124
|
if (!content) return;
|
|
125
|
+
// if (openSplitPane) return;
|
|
124
126
|
|
|
125
127
|
// Add the editor and apply the prior scroll position to the DOM.
|
|
126
128
|
codeEditorRef.current.appendChild(
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const SplitEditorSVG = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
width="24"
|
|
6
|
+
height="24"
|
|
7
|
+
viewBox="0 0 24 24"
|
|
8
|
+
fill="none"
|
|
9
|
+
>
|
|
10
|
+
<rect
|
|
11
|
+
x="5"
|
|
12
|
+
y="4"
|
|
13
|
+
width="7.5"
|
|
14
|
+
height="16"
|
|
15
|
+
rx="1"
|
|
16
|
+
ry="1"
|
|
17
|
+
stroke="currentColor"
|
|
18
|
+
strokeWidth="1.5"
|
|
19
|
+
fill="none"
|
|
20
|
+
/>
|
|
21
|
+
<rect
|
|
22
|
+
x="12"
|
|
23
|
+
y="4"
|
|
24
|
+
width="7.5"
|
|
25
|
+
height="16"
|
|
26
|
+
rx="1"
|
|
27
|
+
ry="1"
|
|
28
|
+
stroke="currentColor"
|
|
29
|
+
strokeWidth="1.5"
|
|
30
|
+
fill="none"
|
|
31
|
+
/>
|
|
32
|
+
</svg>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { useCallback, useContext, useState } from 'react';
|
|
2
2
|
import { PlaySVG } from '../Icons';
|
|
3
|
+
import { SplitEditorSVG } from '../Icons/SplitEditorSVG';
|
|
3
4
|
import { VZCodeContext } from '../VZCodeContext';
|
|
4
5
|
import { OverlayTrigger, Tooltip } from '../bootstrap';
|
|
6
|
+
|
|
5
7
|
import './style.scss';
|
|
6
8
|
|
|
9
|
+
// Feature flag for split pane feature (WIP)
|
|
10
|
+
const enableSplitPane = false;
|
|
11
|
+
|
|
7
12
|
export const RunCodeWidget = ({
|
|
8
13
|
runCodeWidgetTooltipText = (
|
|
9
14
|
<>
|
|
@@ -14,7 +19,7 @@ export const RunCodeWidget = ({
|
|
|
14
19
|
}: {
|
|
15
20
|
runCodeWidgetTooltipText?: JSX.Element;
|
|
16
21
|
}) => {
|
|
17
|
-
const { runCodeRef, runPrettierRef } =
|
|
22
|
+
const { runCodeRef, runPrettierRef, splitCurrentPane } =
|
|
18
23
|
useContext(VZCodeContext);
|
|
19
24
|
const [isRunning, setIsRunning] = useState(false);
|
|
20
25
|
|
|
@@ -37,23 +42,50 @@ export const RunCodeWidget = ({
|
|
|
37
42
|
setTimeout(() => setIsRunning(false), 1000);
|
|
38
43
|
}, []);
|
|
39
44
|
|
|
45
|
+
const handleSplitEditor = useCallback(() => {
|
|
46
|
+
console.log('Split Editor');
|
|
47
|
+
console.log(splitCurrentPane);
|
|
48
|
+
splitCurrentPane();
|
|
49
|
+
}, [splitCurrentPane]);
|
|
50
|
+
|
|
40
51
|
return (
|
|
41
|
-
<div
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
{
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
<div>
|
|
53
|
+
<div className="vz-code-split-view-widget">
|
|
54
|
+
{enableSplitPane && (
|
|
55
|
+
<OverlayTrigger
|
|
56
|
+
placement="bottom"
|
|
57
|
+
overlay={
|
|
58
|
+
<Tooltip id="fork-file-tooltip">
|
|
59
|
+
Split Editor
|
|
60
|
+
</Tooltip>
|
|
61
|
+
}
|
|
62
|
+
>
|
|
63
|
+
<i
|
|
64
|
+
onClick={handleSplitEditor}
|
|
65
|
+
className="icon-button icon-button-dark"
|
|
66
|
+
>
|
|
67
|
+
<SplitEditorSVG />
|
|
68
|
+
</i>
|
|
69
|
+
</OverlayTrigger>
|
|
70
|
+
)}
|
|
71
|
+
</div>
|
|
72
|
+
<div className="vz-code-run-code-widget">
|
|
73
|
+
<OverlayTrigger
|
|
74
|
+
placement="bottom"
|
|
75
|
+
overlay={
|
|
76
|
+
<Tooltip id="run-code-widget-tooltip">
|
|
77
|
+
{runCodeWidgetTooltipText}
|
|
78
|
+
</Tooltip>
|
|
79
|
+
}
|
|
53
80
|
>
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
81
|
+
<i
|
|
82
|
+
className={`icon-button icon-button-dark ${isRunning ? 'rotate-icon' : ''}`}
|
|
83
|
+
onClick={handleClick}
|
|
84
|
+
>
|
|
85
|
+
<PlaySVG />
|
|
86
|
+
</i>
|
|
87
|
+
</OverlayTrigger>
|
|
88
|
+
</div>
|
|
57
89
|
</div>
|
|
58
90
|
);
|
|
59
91
|
};
|
|
@@ -3,11 +3,16 @@
|
|
|
3
3
|
right: 16px;
|
|
4
4
|
top: 9px;
|
|
5
5
|
color: var(--vh-color-neutral-04);
|
|
6
|
-
|
|
7
6
|
.rotate-icon {
|
|
8
7
|
animation: spin 1s linear;
|
|
9
8
|
}
|
|
10
9
|
}
|
|
10
|
+
.vz-code-split-view-widget {
|
|
11
|
+
position: absolute;
|
|
12
|
+
right: 51px;
|
|
13
|
+
top: 9px;
|
|
14
|
+
color: var(--vh-color-neutral-04);
|
|
15
|
+
}
|
|
11
16
|
|
|
12
17
|
@keyframes spin {
|
|
13
18
|
from {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useCallback, useMemo } from 'react';
|
|
2
|
-
import type { FileId } from '../../types';
|
|
3
|
-
import type { TabState } from '../vzReducer';
|
|
2
|
+
import type { FileId, TabState } from '../../types';
|
|
4
3
|
import { CloseSVG } from '../Icons';
|
|
5
4
|
|
|
6
5
|
// Supports adding the file's containing folder to the tab name
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useContext } from 'react';
|
|
2
2
|
import { VZCodeContext } from '../VZCodeContext';
|
|
3
|
-
import { TabState } from '../vzReducer';
|
|
4
3
|
import { Tab } from './Tab';
|
|
5
4
|
import './style.scss';
|
|
5
|
+
import { TabState } from '../../types';
|
|
6
6
|
|
|
7
7
|
// Displays the list of tabs above the code editor.
|
|
8
8
|
export const TabList = () => {
|
|
@@ -14,14 +14,12 @@ import {
|
|
|
14
14
|
VZCodeContent,
|
|
15
15
|
SearchResults,
|
|
16
16
|
SearchFileVisibility,
|
|
17
|
+
TabState,
|
|
18
|
+
PresenceIndicator,
|
|
17
19
|
} from '../types';
|
|
18
20
|
import { usePrettier } from './usePrettier';
|
|
19
21
|
import { useTypeScript } from './useTypeScript';
|
|
20
|
-
import {
|
|
21
|
-
TabState,
|
|
22
|
-
createInitialState,
|
|
23
|
-
vzReducer,
|
|
24
|
-
} from './vzReducer';
|
|
22
|
+
import { createInitialState, vzReducer } from './vzReducer';
|
|
25
23
|
import {
|
|
26
24
|
ThemeLabel,
|
|
27
25
|
defaultTheme,
|
|
@@ -121,6 +119,7 @@ export type VZCodeContextValue = {
|
|
|
121
119
|
id: string,
|
|
122
120
|
visibility: SearchFileVisibility,
|
|
123
121
|
) => void;
|
|
122
|
+
toggleSearchFocused: () => void;
|
|
124
123
|
|
|
125
124
|
isCreateFileModalOpen: boolean;
|
|
126
125
|
handleOpenCreateFileModal: () => void;
|
|
@@ -148,6 +147,13 @@ export type VZCodeContextValue = {
|
|
|
148
147
|
|
|
149
148
|
enableAutoFollow: boolean;
|
|
150
149
|
toggleAutoFollow: () => void;
|
|
150
|
+
|
|
151
|
+
updatePresenceIndicator: (
|
|
152
|
+
presenceIndicator: PresenceIndicator,
|
|
153
|
+
) => void;
|
|
154
|
+
|
|
155
|
+
sidebarPresenceIndicators: Array<PresenceIndicator>;
|
|
156
|
+
splitCurrentPane: () => void;
|
|
151
157
|
};
|
|
152
158
|
|
|
153
159
|
export const VZCodeProvider = ({
|
|
@@ -225,9 +231,10 @@ export const VZCodeProvider = ({
|
|
|
225
231
|
);
|
|
226
232
|
|
|
227
233
|
// Unpack state.
|
|
234
|
+
// print the state object to see what it contains
|
|
235
|
+
// console.log('state: ', state);
|
|
228
236
|
const {
|
|
229
|
-
|
|
230
|
-
activeFileId,
|
|
237
|
+
pane,
|
|
231
238
|
theme,
|
|
232
239
|
search,
|
|
233
240
|
isSearchOpen,
|
|
@@ -236,8 +243,16 @@ export const VZCodeProvider = ({
|
|
|
236
243
|
editorWantsFocus,
|
|
237
244
|
username,
|
|
238
245
|
enableAutoFollow,
|
|
246
|
+
sidebarPresenceIndicators,
|
|
239
247
|
} = state;
|
|
240
248
|
|
|
249
|
+
// TODO support splitPane type
|
|
250
|
+
if (pane.type !== 'leafPane') {
|
|
251
|
+
throw new Error('Expected leafPane');
|
|
252
|
+
}
|
|
253
|
+
const tabList = pane.tabList;
|
|
254
|
+
const activeFileId = pane.activeFileId;
|
|
255
|
+
|
|
241
256
|
// Functions for dispatching actions to the reducer.
|
|
242
257
|
const {
|
|
243
258
|
setActiveFileId,
|
|
@@ -250,6 +265,7 @@ export const VZCodeProvider = ({
|
|
|
250
265
|
setSearch,
|
|
251
266
|
setSearchResults,
|
|
252
267
|
setSearchFileVisibility,
|
|
268
|
+
toggleSearchFocused,
|
|
253
269
|
setIsSettingsOpen,
|
|
254
270
|
setIsDocOpen,
|
|
255
271
|
closeSettings,
|
|
@@ -257,6 +273,8 @@ export const VZCodeProvider = ({
|
|
|
257
273
|
editorNoLongerWantsFocus,
|
|
258
274
|
setUsername,
|
|
259
275
|
toggleAutoFollow,
|
|
276
|
+
updatePresenceIndicator,
|
|
277
|
+
splitCurrentPane,
|
|
260
278
|
} = useActions(dispatch);
|
|
261
279
|
|
|
262
280
|
// Sync tab state to the URL.
|
|
@@ -344,6 +362,7 @@ export const VZCodeProvider = ({
|
|
|
344
362
|
handleOpenCreateFileModal,
|
|
345
363
|
setActiveFileLeft,
|
|
346
364
|
setActiveFileRight,
|
|
365
|
+
toggleSearchFocused,
|
|
347
366
|
runPrettierRef,
|
|
348
367
|
runCodeRef,
|
|
349
368
|
sidebarRef,
|
|
@@ -386,6 +405,7 @@ export const VZCodeProvider = ({
|
|
|
386
405
|
setSearch,
|
|
387
406
|
setSearchResults,
|
|
388
407
|
setSearchFileVisibility,
|
|
408
|
+
toggleSearchFocused,
|
|
389
409
|
|
|
390
410
|
isSettingsOpen,
|
|
391
411
|
setIsSettingsOpen,
|
|
@@ -432,6 +452,9 @@ export const VZCodeProvider = ({
|
|
|
432
452
|
|
|
433
453
|
enableAutoFollow,
|
|
434
454
|
toggleAutoFollow,
|
|
455
|
+
updatePresenceIndicator,
|
|
456
|
+
sidebarPresenceIndicators,
|
|
457
|
+
splitCurrentPane,
|
|
435
458
|
};
|
|
436
459
|
|
|
437
460
|
return (
|
|
@@ -23,6 +23,7 @@ export const DirectoryListing = ({
|
|
|
23
23
|
deleteDirectory,
|
|
24
24
|
isDirectoryOpen,
|
|
25
25
|
toggleDirectory,
|
|
26
|
+
sidebarPresenceIndicators,
|
|
26
27
|
} = useContext(VZCodeContext);
|
|
27
28
|
|
|
28
29
|
const handleClick = useCallback(() => {
|
|
@@ -63,7 +64,7 @@ export const DirectoryListing = ({
|
|
|
63
64
|
>
|
|
64
65
|
<DirectoryArrowSVG />
|
|
65
66
|
</div>
|
|
66
|
-
{name}
|
|
67
|
+
<div className="name">{name}</div>
|
|
67
68
|
</Item>
|
|
68
69
|
{children && isOpen ? (
|
|
69
70
|
<div className="indentation">
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useContext } from 'react';
|
|
2
2
|
import { Item } from './Item';
|
|
3
|
-
import { FileId } from '../../types';
|
|
3
|
+
import { FileId, PresenceIndicator } from '../../types';
|
|
4
4
|
import {
|
|
5
5
|
FileSVG,
|
|
6
6
|
JavaScriptSVG,
|
|
@@ -46,12 +46,14 @@ export const FileListing = ({
|
|
|
46
46
|
handleFileClick,
|
|
47
47
|
handleFileDoubleClick,
|
|
48
48
|
isActive,
|
|
49
|
+
presence,
|
|
49
50
|
}: {
|
|
50
51
|
name: string;
|
|
51
52
|
fileId: FileId;
|
|
52
53
|
handleFileClick: (fileId: FileId) => void;
|
|
53
54
|
handleFileDoubleClick: (fileId: FileId) => void;
|
|
54
55
|
isActive: boolean;
|
|
56
|
+
presence: PresenceIndicator[];
|
|
55
57
|
}) => {
|
|
56
58
|
const { renameFile, deleteFile } =
|
|
57
59
|
useContext(VZCodeContext);
|
|
@@ -85,8 +87,26 @@ export const FileListing = ({
|
|
|
85
87
|
handleRenameClick={handleRenameClick}
|
|
86
88
|
isActive={isActive}
|
|
87
89
|
>
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
+
<div className="name">
|
|
91
|
+
{/* Render presence indicators to the left of the file name */}
|
|
92
|
+
{presence.length > 0 && (
|
|
93
|
+
<div className="presence-indicators">
|
|
94
|
+
{presence.map((indicator, index) => (
|
|
95
|
+
<div
|
|
96
|
+
key={index}
|
|
97
|
+
className="presence-indicator"
|
|
98
|
+
>
|
|
99
|
+
{indicator.username[0]}{' '}
|
|
100
|
+
{/* Display the first letter of the username */}
|
|
101
|
+
</div>
|
|
102
|
+
))}
|
|
103
|
+
</div>
|
|
104
|
+
)}
|
|
105
|
+
<i className="file-icon">
|
|
106
|
+
{getExtensionIcon(name)}
|
|
107
|
+
</i>
|
|
108
|
+
{name}
|
|
109
|
+
</div>
|
|
90
110
|
</Item>
|
|
91
111
|
);
|
|
92
112
|
};
|
|
@@ -13,9 +13,9 @@ import { DeleteConfirmationModal } from './DeleteConfirmationModal';
|
|
|
13
13
|
import { VZCodeContext } from '../VZCodeContext';
|
|
14
14
|
import { ItemId } from '../../types';
|
|
15
15
|
import { getExtensionIcon } from './FileListing';
|
|
16
|
+
import { SplitEditorSVG } from '../Icons/SplitEditorSVG';
|
|
17
|
+
import { SplitPaneResizeProvider } from '../SplitPaneResizeContext';
|
|
16
18
|
|
|
17
|
-
// TODO support renaming directories
|
|
18
|
-
// See https://github.com/vizhub-core/vzcode/issues/103
|
|
19
19
|
const enableRenameDirectory = true;
|
|
20
20
|
|
|
21
21
|
// A file or directory in the sidebar.
|
|
@@ -19,10 +19,16 @@ export const Listing = ({
|
|
|
19
19
|
handleFileClick: (fileId: FileId) => void;
|
|
20
20
|
handleFileDoubleClick: (fileId: FileId) => void;
|
|
21
21
|
}) => {
|
|
22
|
-
const { activeFileId } =
|
|
22
|
+
const { activeFileId, sidebarPresenceIndicators } =
|
|
23
|
+
useContext(VZCodeContext);
|
|
23
24
|
const { name, file, fileId } = entity as FileTreeFile;
|
|
24
25
|
const { path, children } = entity as FileTree;
|
|
25
26
|
|
|
27
|
+
const presence =
|
|
28
|
+
sidebarPresenceIndicators?.filter(
|
|
29
|
+
(indicator) => indicator.fileId === fileId,
|
|
30
|
+
) || [];
|
|
31
|
+
|
|
26
32
|
return file ? (
|
|
27
33
|
<FileListing
|
|
28
34
|
fileId={fileId}
|
|
@@ -30,6 +36,7 @@ export const Listing = ({
|
|
|
30
36
|
handleFileClick={handleFileClick}
|
|
31
37
|
handleFileDoubleClick={handleFileDoubleClick}
|
|
32
38
|
isActive={fileId === activeFileId}
|
|
39
|
+
presence={presence}
|
|
33
40
|
/>
|
|
34
41
|
) : (
|
|
35
42
|
<DirectoryListing
|
|
@@ -46,7 +46,7 @@ export const Search = () => {
|
|
|
46
46
|
shareDBDoc,
|
|
47
47
|
editorCache,
|
|
48
48
|
} = useContext(VZCodeContext);
|
|
49
|
-
const { pattern, results } = search;
|
|
49
|
+
const { pattern, results, focused } = search;
|
|
50
50
|
|
|
51
51
|
useEffect(() => {
|
|
52
52
|
if (isMounted) {
|
|
@@ -67,10 +67,15 @@ export const Search = () => {
|
|
|
67
67
|
return () => clearTimeout(delaySearch);
|
|
68
68
|
} else {
|
|
69
69
|
setIsMounted(true);
|
|
70
|
-
inputRef.current.focus();
|
|
71
70
|
}
|
|
72
71
|
}, [pattern]);
|
|
73
72
|
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (inputRef.current) {
|
|
75
|
+
inputRef.current.focus();
|
|
76
|
+
}
|
|
77
|
+
}, [focused]);
|
|
78
|
+
|
|
74
79
|
return (
|
|
75
80
|
<div>
|
|
76
81
|
<Form.Group
|