vzcode 1.10.0 → 1.12.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-DXNuoOqQ.js → index-4cYZN5Uf.js} +57 -57
- package/dist/assets/{index--bvX81xc.js → index-CvSVuMne.js} +57 -57
- package/dist/assets/worker-CJWrLBN2.js +286 -0
- package/dist/index.html +1 -1
- package/package.json +13 -12
- package/src/client/AIAssist/AIAssistCodeMirrorKeyMap.ts +5 -2
- package/src/client/AIAssist/startAIAssist.ts +1 -1
- package/src/client/CodeEditor/getOrCreateEditor.ts +4 -0
- package/src/client/CodeEditor/index.tsx +2 -0
- package/src/client/CodeEditor/json1PresenceDisplay.ts +128 -90
- package/src/client/TabList/Tab.tsx +1 -2
- package/src/client/TabList/index.tsx +1 -1
- package/src/client/VZCodeContext.tsx +17 -14
- package/src/client/VZKeyboardShortcutsDoc.tsx +2 -1
- package/src/client/VZSidebar/Search.tsx +7 -2
- package/src/client/VZSidebar/index.tsx +87 -24
- package/src/client/tabsSearchParameters.ts +1 -2
- package/src/client/useActions.ts +10 -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 +8 -3
- package/src/client/vzReducer/findPane.ts +19 -0
- package/src/client/vzReducer/index.ts +17 -25
- 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/updatePane.ts +44 -0
- package/src/server/index.js +1 -0
- package/src/types.ts +69 -70
- package/dist/assets/worker-Cs0ZU3m1.js +0 -291
package/dist/index.html
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
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-
|
|
23
|
+
<script type="module" crossorigin src="/assets/index-4cYZN5Uf.js"></script>
|
|
24
24
|
<link rel="stylesheet" crossorigin href="/assets/index-BKRpkJER.css">
|
|
25
25
|
</head>
|
|
26
26
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vzcode",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.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.28.
|
|
114
|
+
"@codemirror/view": "^6.28.6",
|
|
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,12 +140,12 @@
|
|
|
140
140
|
"json0-ot-diff": "^1.1.2",
|
|
141
141
|
"ngrok": "^4.3.3",
|
|
142
142
|
"open": "^10.1.0",
|
|
143
|
-
"openai": "^4.52.
|
|
144
|
-
"prettier-plugin-svelte": "^3.2.
|
|
143
|
+
"openai": "^4.52.7",
|
|
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.
|
|
148
|
+
"react-router-dom": "^6.25.1",
|
|
149
149
|
"sharedb": "^5.0.2",
|
|
150
150
|
"sharedb-client-browser": "^5.0.2",
|
|
151
151
|
"vizhub-ui": "^3.23.0",
|
|
@@ -156,11 +156,12 @@
|
|
|
156
156
|
"@types/react-dom": "^18.3.0",
|
|
157
157
|
"@vitejs/plugin-react": "^4.3.1",
|
|
158
158
|
"concurrently": "^8.2.2",
|
|
159
|
-
"
|
|
160
|
-
"
|
|
159
|
+
"cross-env": "^7.0.3",
|
|
160
|
+
"prettier": "^3.3.3",
|
|
161
|
+
"sass": "^1.77.8",
|
|
161
162
|
"typescript": "^5.5.3",
|
|
162
|
-
"vite": "^5.3.
|
|
163
|
-
"vitest": "^2.0.
|
|
163
|
+
"vite": "^5.3.4",
|
|
164
|
+
"vitest": "^2.0.3"
|
|
164
165
|
},
|
|
165
166
|
"optionalDependencies": {
|
|
166
167
|
"@rollup/rollup-darwin-arm64": "^4.18.1",
|
|
@@ -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,
|
|
@@ -19,6 +19,7 @@ import { json1Presence, textUnicode } from '../../ot';
|
|
|
19
19
|
import {
|
|
20
20
|
FileId,
|
|
21
21
|
ShareDBDoc,
|
|
22
|
+
TabState,
|
|
22
23
|
Username,
|
|
23
24
|
VZCodeContent,
|
|
24
25
|
} from '../../types';
|
|
@@ -115,6 +116,7 @@ export const getOrCreateEditor = ({
|
|
|
115
116
|
customInteractRules,
|
|
116
117
|
allowGlobals,
|
|
117
118
|
enableAutoFollowRef,
|
|
119
|
+
openTab,
|
|
118
120
|
}: {
|
|
119
121
|
fileId: FileId;
|
|
120
122
|
|
|
@@ -148,6 +150,7 @@ export const getOrCreateEditor = ({
|
|
|
148
150
|
// Ref to a boolean that determines whether to
|
|
149
151
|
// enable auto-following the cursors of remote users.
|
|
150
152
|
enableAutoFollowRef: React.MutableRefObject<boolean>;
|
|
153
|
+
openTab: (tabState: TabState) => void;
|
|
151
154
|
}): EditorCacheValue => {
|
|
152
155
|
// Cache hit
|
|
153
156
|
if (editorCache.has(fileId)) {
|
|
@@ -202,6 +205,7 @@ export const getOrCreateEditor = ({
|
|
|
202
205
|
path: textPath,
|
|
203
206
|
docPresence,
|
|
204
207
|
enableAutoFollowRef,
|
|
208
|
+
openTab,
|
|
205
209
|
}),
|
|
206
210
|
);
|
|
207
211
|
}
|
|
@@ -38,6 +38,7 @@ export const CodeEditor = ({
|
|
|
38
38
|
theme,
|
|
39
39
|
codeEditorRef,
|
|
40
40
|
enableAutoFollow,
|
|
41
|
+
openTab,
|
|
41
42
|
} = useContext(VZCodeContext);
|
|
42
43
|
|
|
43
44
|
// Set `doc.data.isInteracting` to `true` when the user is interacting
|
|
@@ -97,6 +98,7 @@ export const CodeEditor = ({
|
|
|
97
98
|
customInteractRules,
|
|
98
99
|
allowGlobals,
|
|
99
100
|
enableAutoFollowRef,
|
|
101
|
+
openTab,
|
|
100
102
|
}),
|
|
101
103
|
[
|
|
102
104
|
activeFileId,
|
|
@@ -6,7 +6,13 @@ import {
|
|
|
6
6
|
} from '@codemirror/view';
|
|
7
7
|
import { Annotation, RangeSet } from '@codemirror/state';
|
|
8
8
|
import ColorHash from 'color-hash';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
FileId,
|
|
11
|
+
Presence,
|
|
12
|
+
PresenceId,
|
|
13
|
+
TabState,
|
|
14
|
+
Username,
|
|
15
|
+
} from '../../types';
|
|
10
16
|
|
|
11
17
|
const debug = false;
|
|
12
18
|
|
|
@@ -26,6 +32,12 @@ export const json1PresenceDisplay = ({
|
|
|
26
32
|
path,
|
|
27
33
|
docPresence,
|
|
28
34
|
enableAutoFollowRef,
|
|
35
|
+
openTab,
|
|
36
|
+
}: {
|
|
37
|
+
path: Array<string>;
|
|
38
|
+
docPresence: any;
|
|
39
|
+
enableAutoFollowRef: React.MutableRefObject<boolean>;
|
|
40
|
+
openTab: (tabState: TabState) => void;
|
|
29
41
|
}) => [
|
|
30
42
|
ViewPlugin.fromClass(
|
|
31
43
|
class {
|
|
@@ -44,113 +56,139 @@ export const json1PresenceDisplay = ({
|
|
|
44
56
|
// Mutable state local to this closure representing aggregated presence.
|
|
45
57
|
// * Keys are presence ids
|
|
46
58
|
// * Values are presence objects as defined by ot-json1-presence
|
|
47
|
-
const presenceState =
|
|
59
|
+
const presenceState: Record<PresenceId, Presence> =
|
|
60
|
+
{};
|
|
48
61
|
|
|
49
62
|
// Add the scroll event listener
|
|
50
63
|
//This runs for the arrow key scrolling, it should result in the users scrolling to eachother's location.
|
|
51
|
-
view.dom.addEventListener('scroll', () => {
|
|
52
|
-
|
|
53
|
-
});
|
|
64
|
+
// view.dom.addEventListener('scroll', () => {
|
|
65
|
+
// this.scrollToCursor(view);
|
|
66
|
+
// });
|
|
54
67
|
// Receive remote presence changes.
|
|
55
|
-
docPresence.on(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
68
|
+
docPresence.on(
|
|
69
|
+
'receive',
|
|
70
|
+
(id: PresenceId, presence: Presence) => {
|
|
71
|
+
if (debug) {
|
|
72
|
+
console.log(
|
|
73
|
+
`Received presence for id ${id}`,
|
|
74
|
+
presence,
|
|
75
|
+
); // Debug statement
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// If presence === null, the user has disconnected / exited
|
|
79
|
+
if (!presence) {
|
|
80
|
+
delete presenceState[id];
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Check if the presence is for the current file or not.
|
|
85
|
+
const isPresenceInCurrentFile = pathMatches(
|
|
86
|
+
path,
|
|
59
87
|
presence,
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
// Update decorations to reflect new presence state.
|
|
70
|
-
// TODO consider mutating this rather than recomputing it on each change.
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
// If the presence is in the current file, update the presence state.
|
|
91
|
+
if (isPresenceInCurrentFile) {
|
|
92
|
+
presenceState[id] = presence;
|
|
93
|
+
} else if (presence) {
|
|
94
|
+
// Otherwise, delete the presence state.
|
|
95
|
+
delete presenceState[id];
|
|
71
96
|
|
|
72
|
-
|
|
97
|
+
// If auto-follow is enabled, and the presence is NOT
|
|
98
|
+
// in the current file, then open the tab of the other user.
|
|
99
|
+
if (enableAutoFollowRef.current) {
|
|
100
|
+
openTab({
|
|
101
|
+
fileId: presence.start[1] as FileId,
|
|
102
|
+
isTransient: true,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Update decorations to reflect new presence state.
|
|
107
|
+
// TODO consider mutating this rather than recomputing it on each change.
|
|
73
108
|
|
|
74
|
-
|
|
75
|
-
for (const id of Object.keys(presenceState)) {
|
|
76
|
-
const presence = presenceState[id];
|
|
77
|
-
const { start, end } = presence;
|
|
78
|
-
const from = start[start.length - 1];
|
|
79
|
-
const to = end[end.length - 1];
|
|
80
|
-
const userColor = new ColorHash({
|
|
81
|
-
lightness: 0.75,
|
|
82
|
-
}).rgb(id);
|
|
83
|
-
const { username } = presence;
|
|
109
|
+
const presenceDecorations = [];
|
|
84
110
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// id,
|
|
96
|
-
'' + Math.random(),
|
|
97
|
-
userColor,
|
|
98
|
-
username,
|
|
99
|
-
),
|
|
100
|
-
}),
|
|
101
|
-
});
|
|
111
|
+
// Object.keys(presenceState).map((id) => {
|
|
112
|
+
for (const id of Object.keys(presenceState)) {
|
|
113
|
+
const presence: Presence = presenceState[id];
|
|
114
|
+
const { start, end } = presence;
|
|
115
|
+
const from = +start[start.length - 1];
|
|
116
|
+
const to = +end[end.length - 1];
|
|
117
|
+
const userColor = new ColorHash({
|
|
118
|
+
lightness: 0.75,
|
|
119
|
+
}).rgb(id);
|
|
120
|
+
const { username } = presence;
|
|
102
121
|
|
|
103
|
-
// This is `true` when the presence is a cursor,
|
|
104
|
-
// with no selection.
|
|
105
|
-
if (from !== to) {
|
|
106
|
-
// This is the case when the presence is a selection.
|
|
107
122
|
presenceDecorations.push({
|
|
108
123
|
from,
|
|
109
|
-
to,
|
|
110
|
-
value: Decoration.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
124
|
+
to: from,
|
|
125
|
+
value: Decoration.widget({
|
|
126
|
+
side: -1,
|
|
127
|
+
block: false,
|
|
128
|
+
widget: new PresenceWidget(
|
|
129
|
+
// TODO see if we can figure out why
|
|
130
|
+
// updateDOM was not being called when passing
|
|
131
|
+
// the presence id as the id
|
|
132
|
+
// id,
|
|
133
|
+
'' + Math.random(),
|
|
134
|
+
userColor,
|
|
135
|
+
username,
|
|
136
|
+
),
|
|
137
|
+
}),
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// This is `true` when the presence is a cursor,
|
|
141
|
+
// with no selection.
|
|
142
|
+
if (from !== to) {
|
|
143
|
+
// This is the case when the presence is a selection.
|
|
144
|
+
presenceDecorations.push({
|
|
145
|
+
from,
|
|
146
|
+
to,
|
|
147
|
+
value: Decoration.mark({
|
|
148
|
+
class: 'cm-json1-presence',
|
|
149
|
+
attributes: {
|
|
150
|
+
style: `
|
|
114
151
|
background-color: rgba(${userColor}, 0.75);
|
|
115
152
|
mix-blend-mode: luminosity;
|
|
116
153
|
`,
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
154
|
+
},
|
|
155
|
+
}),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
if (view.state.doc.length >= from) {
|
|
159
|
+
// Ensure position is valid
|
|
160
|
+
this.cursorPosition[id] = from; // Store the cursor position, important to run if we cant get the regular scroll to work
|
|
161
|
+
// console.log(`Stored cursor position for id ${id}: ${from}`); // Debug statement
|
|
162
|
+
} else {
|
|
163
|
+
// console.warn(`Invalid cursor position for id ${id}: ${from}`); // Debug statement
|
|
164
|
+
}
|
|
120
165
|
}
|
|
121
|
-
if (view.state.doc.length >= from) {
|
|
122
|
-
// Ensure position is valid
|
|
123
|
-
this.cursorPosition[id] = from; // Store the cursor position, important to run if we cant get the regular scroll to work
|
|
124
|
-
// console.log(`Stored cursor position for id ${id}: ${from}`); // Debug statement
|
|
125
|
-
} else {
|
|
126
|
-
// console.warn(`Invalid cursor position for id ${id}: ${from}`); // Debug statement
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
166
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
167
|
+
this.decorations = Decoration.set(
|
|
168
|
+
presenceDecorations,
|
|
169
|
+
// Without this argument, we get the following error:
|
|
170
|
+
// Uncaught Error: Ranges must be added sorted by `from` position and `startSide`
|
|
171
|
+
true,
|
|
172
|
+
);
|
|
136
173
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
174
|
+
// Somehow this triggers re-rendering of the Decorations.
|
|
175
|
+
// Not sure if this is the correct usage of the API.
|
|
176
|
+
// Inspired by https://github.com/yjs/y-codemirror.next/blob/main/src/y-remote-selections.js
|
|
177
|
+
// Set timeout so that the current CodeMirror update finishes
|
|
178
|
+
// before the next ones that render presence begin.
|
|
179
|
+
setTimeout(() => {
|
|
180
|
+
view.dispatch({
|
|
181
|
+
annotations: [presenceAnnotation.of(true)],
|
|
182
|
+
});
|
|
183
|
+
}, 0);
|
|
147
184
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
185
|
+
// Auto-follow all users when their presence is broadcast
|
|
186
|
+
// by scrolling them into view.
|
|
187
|
+
if (enableAutoFollowRef.current) {
|
|
188
|
+
this.scrollToCursor(view);
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
);
|
|
154
192
|
}
|
|
155
193
|
// Method to scroll the view to keep the cursor in view
|
|
156
194
|
scrollToCursor(view) {
|
|
@@ -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,11 @@ import {
|
|
|
14
14
|
VZCodeContent,
|
|
15
15
|
SearchResults,
|
|
16
16
|
SearchFileVisibility,
|
|
17
|
+
TabState,
|
|
17
18
|
} from '../types';
|
|
18
19
|
import { usePrettier } from './usePrettier';
|
|
19
20
|
import { useTypeScript } from './useTypeScript';
|
|
20
|
-
import {
|
|
21
|
-
TabState,
|
|
22
|
-
createInitialState,
|
|
23
|
-
vzReducer,
|
|
24
|
-
} from './vzReducer';
|
|
21
|
+
import { createInitialState, vzReducer } from './vzReducer';
|
|
25
22
|
import {
|
|
26
23
|
ThemeLabel,
|
|
27
24
|
defaultTheme,
|
|
@@ -52,7 +49,9 @@ export type VZCodeContextValue = {
|
|
|
52
49
|
submitOperation: (
|
|
53
50
|
next: (content: VZCodeContent) => VZCodeContent,
|
|
54
51
|
) => void;
|
|
52
|
+
// TODO pull in this type from ShareDB if possible
|
|
55
53
|
localPresence: any;
|
|
54
|
+
// TODO pull in this type from ShareDB if possible
|
|
56
55
|
docPresence: any;
|
|
57
56
|
|
|
58
57
|
files: Files | null;
|
|
@@ -76,13 +75,7 @@ export type VZCodeContextValue = {
|
|
|
76
75
|
setActiveFileRight: () => void;
|
|
77
76
|
|
|
78
77
|
tabList: Array<TabState>;
|
|
79
|
-
openTab: (
|
|
80
|
-
fileId,
|
|
81
|
-
isTransient,
|
|
82
|
-
}: {
|
|
83
|
-
fileId: string;
|
|
84
|
-
isTransient?: boolean;
|
|
85
|
-
}) => void;
|
|
78
|
+
openTab: (tabState: TabState) => void;
|
|
86
79
|
closeTabs: (fileIds: string[]) => void;
|
|
87
80
|
|
|
88
81
|
isSettingsOpen: boolean;
|
|
@@ -125,6 +118,7 @@ export type VZCodeContextValue = {
|
|
|
125
118
|
id: string,
|
|
126
119
|
visibility: SearchFileVisibility,
|
|
127
120
|
) => void;
|
|
121
|
+
toggleSearchFocused: () => void;
|
|
128
122
|
|
|
129
123
|
isCreateFileModalOpen: boolean;
|
|
130
124
|
handleOpenCreateFileModal: () => void;
|
|
@@ -230,8 +224,7 @@ export const VZCodeProvider = ({
|
|
|
230
224
|
|
|
231
225
|
// Unpack state.
|
|
232
226
|
const {
|
|
233
|
-
|
|
234
|
-
activeFileId,
|
|
227
|
+
pane,
|
|
235
228
|
theme,
|
|
236
229
|
search,
|
|
237
230
|
isSearchOpen,
|
|
@@ -242,6 +235,13 @@ export const VZCodeProvider = ({
|
|
|
242
235
|
enableAutoFollow,
|
|
243
236
|
} = state;
|
|
244
237
|
|
|
238
|
+
// TODO support splitPane type
|
|
239
|
+
if (pane.type !== 'leafPane') {
|
|
240
|
+
throw new Error('Expected leafPane');
|
|
241
|
+
}
|
|
242
|
+
const tabList = pane.tabList;
|
|
243
|
+
const activeFileId = pane.activeFileId;
|
|
244
|
+
|
|
245
245
|
// Functions for dispatching actions to the reducer.
|
|
246
246
|
const {
|
|
247
247
|
setActiveFileId,
|
|
@@ -254,6 +254,7 @@ export const VZCodeProvider = ({
|
|
|
254
254
|
setSearch,
|
|
255
255
|
setSearchResults,
|
|
256
256
|
setSearchFileVisibility,
|
|
257
|
+
toggleSearchFocused,
|
|
257
258
|
setIsSettingsOpen,
|
|
258
259
|
setIsDocOpen,
|
|
259
260
|
closeSettings,
|
|
@@ -348,6 +349,7 @@ export const VZCodeProvider = ({
|
|
|
348
349
|
handleOpenCreateFileModal,
|
|
349
350
|
setActiveFileLeft,
|
|
350
351
|
setActiveFileRight,
|
|
352
|
+
toggleSearchFocused,
|
|
351
353
|
runPrettierRef,
|
|
352
354
|
runCodeRef,
|
|
353
355
|
sidebarRef,
|
|
@@ -390,6 +392,7 @@ export const VZCodeProvider = ({
|
|
|
390
392
|
setSearch,
|
|
391
393
|
setSearchResults,
|
|
392
394
|
setSearchFileVisibility,
|
|
395
|
+
toggleSearchFocused,
|
|
393
396
|
|
|
394
397
|
isSettingsOpen,
|
|
395
398
|
setIsSettingsOpen,
|
|
@@ -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
|