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.
Files changed (48) hide show
  1. package/README.md +1 -1
  2. package/dist/assets/index-Bm65AzMP.js +425 -0
  3. package/dist/assets/index-BpeGnq7b.js +157 -0
  4. package/dist/assets/{index-BKRpkJER.css → index-Wxh-Jaj6.css} +1 -1
  5. package/dist/assets/worker-CJWrLBN2.js +286 -0
  6. package/dist/index.html +2 -2
  7. package/package.json +18 -17
  8. package/src/client/AIAssist/AIAssistCodeMirrorKeyMap.ts +5 -2
  9. package/src/client/AIAssist/startAIAssist.ts +1 -1
  10. package/src/client/CodeEditor/getOrCreateEditor.ts +11 -4
  11. package/src/client/CodeEditor/index.tsx +2 -0
  12. package/src/client/CodeEditor/json1PresenceDisplay.ts +1 -1
  13. package/src/client/Icons/SplitEditorSVG.tsx +34 -0
  14. package/src/client/RunCodeWidget/index.tsx +48 -16
  15. package/src/client/RunCodeWidget/style.scss +6 -1
  16. package/src/client/TabList/Tab.tsx +1 -2
  17. package/src/client/TabList/index.tsx +1 -1
  18. package/src/client/VZCodeContext.tsx +30 -7
  19. package/src/client/VZKeyboardShortcutsDoc.tsx +2 -1
  20. package/src/client/VZSidebar/DirectoryListing.tsx +2 -1
  21. package/src/client/VZSidebar/FileListing.tsx +23 -3
  22. package/src/client/VZSidebar/Item.tsx +2 -2
  23. package/src/client/VZSidebar/Listing.tsx +8 -1
  24. package/src/client/VZSidebar/Search.tsx +7 -2
  25. package/src/client/VZSidebar/index.tsx +130 -24
  26. package/src/client/VZSidebar/styles.scss +25 -0
  27. package/src/client/tabsSearchParameters.ts +1 -2
  28. package/src/client/useActions.ts +29 -1
  29. package/src/client/useKeyboardShortcuts.ts +31 -1
  30. package/src/client/useURLSync.ts +1 -2
  31. package/src/client/vzReducer/closeTabReducer.test.ts +49 -33
  32. package/src/client/vzReducer/closeTabsReducer.ts +51 -19
  33. package/src/client/vzReducer/createInitialState.ts +9 -3
  34. package/src/client/vzReducer/findPane.ts +19 -0
  35. package/src/client/vzReducer/index.ts +38 -27
  36. package/src/client/vzReducer/openTabReducer.test.ts +38 -21
  37. package/src/client/vzReducer/openTabReducer.ts +26 -10
  38. package/src/client/vzReducer/searchReducer.ts +21 -1
  39. package/src/client/vzReducer/setActiveFileIdReducer.ts +23 -4
  40. package/src/client/vzReducer/setActiveFileLeftRightReducer.ts +37 -8
  41. package/src/client/vzReducer/splitCurrentPaneReducer.ts +71 -0
  42. package/src/client/vzReducer/updatePane.ts +44 -0
  43. package/src/client/vzReducer/updatePresenceIndicatorReducer.ts +30 -0
  44. package/src/server/index.js +1 -0
  45. package/src/types.ts +86 -1
  46. package/dist/assets/index--bvX81xc.js +0 -425
  47. package/dist/assets/index-BSuijdti.js +0 -157
  48. package/dist/assets/worker-Cs0ZU3m1.js +0 -291
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  useCallback,
3
3
  useContext,
4
+ useEffect,
4
5
  useMemo,
5
6
  useState,
6
7
  } from 'react';
@@ -8,6 +9,9 @@ import {
8
9
  FileId,
9
10
  FileTree,
10
11
  FileTreeFile,
12
+ Presence,
13
+ PresenceId,
14
+ PresenceIndicator,
11
15
  } from '../../types';
12
16
  import { Tooltip, OverlayTrigger } from '../bootstrap';
13
17
  import { Search } from './Search';
@@ -35,21 +39,74 @@ import './styles.scss';
35
39
  const enableConnectionStatus = true;
36
40
 
37
41
  export const VZSidebar = ({
38
- createFileTooltipText = 'New File',
39
- createDirTooltipText = 'New Directory',
40
- openSettingsTooltipText = 'Open Settings',
41
- openKeyboardShortcuts = 'Keyboard Shortcuts',
42
- reportBugTooltipText = 'Report Bug',
43
- searchToolTipText = 'Search',
44
- filesToolTipText = 'Files',
42
+ createFileTooltipText = (
43
+ <>
44
+ <strong>New file</strong>
45
+ {/* TODO verify that Ctrl + Shift + N works - does not work in Linux */}
46
+ <div>(Alt + N or Ctrl + Shift + N)</div>
47
+ </>
48
+ ),
49
+ createDirTooltipText = (
50
+ <div>
51
+ <strong>New Directory</strong>
52
+ {/* TODO consider Alt+D, as Ctrl + Shift + D does not work in Linux */}
53
+ <div>(Ctrl + Shift + D)</div>
54
+ </div>
55
+ ),
56
+ openSettingsTooltipText = (
57
+ <div>
58
+ <strong>Open Settings</strong>
59
+ <div>(Ctrl + Shift + S or Ctrl + ,)</div>
60
+ </div>
61
+ ),
62
+ openKeyboardShortcuts = (
63
+ <div>
64
+ <strong>Keyboard Shortcuts</strong>
65
+ <div>(Ctrl + Shift + K)</div>
66
+ </div>
67
+ ),
68
+ reportBugTooltipText = (
69
+ <div>
70
+ <strong>Report Bug</strong>
71
+ {/* TODO get this keyboard shortcut working? */}
72
+ {/* <div>(Ctrl + Shift + B)</div> */}
73
+ </div>
74
+ ),
75
+ searchToolTipText = (
76
+ <div>
77
+ <strong>Search</strong>
78
+ <div>(Ctrl + Shift + F)</div>
79
+ </div>
80
+ ),
81
+ filesToolTipText = (
82
+ <div>
83
+ <strong>Files</strong>
84
+ <div>(Ctrl + Shift + E)</div>
85
+ </div>
86
+ ),
87
+ enableAutoFollowTooltipText = (
88
+ <div>
89
+ <strong>Enable Auto Follow</strong>
90
+ <div>(Ctrl + Shift + A)</div>
91
+ </div>
92
+ ),
93
+ disableAutoFollowTooltipText = (
94
+ <div>
95
+ <strong>Disable Auto Follow</strong>
96
+ {/* TODO consider Alt+A, this breaks in Linux */}
97
+ <div>(Ctrl + Shift + A)</div>
98
+ </div>
99
+ ),
45
100
  }: {
46
- createFileTooltipText?: string;
47
- createDirTooltipText?: string;
48
- openSettingsTooltipText?: string;
49
- reportBugTooltipText?: string;
50
- openKeyboardShortcuts?: string;
51
- searchToolTipText?: string;
52
- filesToolTipText?: string;
101
+ createFileTooltipText?: React.ReactNode;
102
+ createDirTooltipText?: React.ReactNode;
103
+ openSettingsTooltipText?: React.ReactNode;
104
+ reportBugTooltipText?: React.ReactNode;
105
+ openKeyboardShortcuts?: React.ReactNode;
106
+ searchToolTipText?: React.ReactNode;
107
+ filesToolTipText?: React.ReactNode;
108
+ enableAutoFollowTooltipText?: React.ReactNode;
109
+ disableAutoFollowTooltipText?: React.ReactNode;
53
110
  }) => {
54
111
  const {
55
112
  files,
@@ -64,6 +121,9 @@ export const VZSidebar = ({
64
121
  sidebarRef,
65
122
  enableAutoFollow,
66
123
  toggleAutoFollow,
124
+ docPresence,
125
+ updatePresenceIndicator,
126
+ sidebarPresenceIndicators,
67
127
  } = useContext(VZCodeContext);
68
128
 
69
129
  const fileTree = useMemo(
@@ -111,6 +171,42 @@ export const VZSidebar = ({
111
171
  handleDrop,
112
172
  } = useDragAndDrop();
113
173
 
174
+ // Track presence of remote users across files
175
+ // so that they can be displayed in the sidebar.
176
+ useEffect(() => {
177
+ docPresence;
178
+ }, []);
179
+
180
+ // Track the presence indicators for display in sidebar
181
+ useEffect(() => {
182
+ if (docPresence) {
183
+ const handleReceive = (
184
+ presenceId: PresenceId,
185
+ update: Presence,
186
+ ) => {
187
+ const presenceIndicator: PresenceIndicator = {
188
+ username: update.username,
189
+ fileId: update.start[1] as FileId,
190
+ };
191
+
192
+ console.log('Got presence!');
193
+ // console.log({presenceId,update})
194
+ console.log(
195
+ JSON.stringify(presenceIndicator, null, 2),
196
+ );
197
+
198
+ updatePresenceIndicator(presenceIndicator);
199
+ };
200
+
201
+ docPresence.on('receive', handleReceive);
202
+ return () => {
203
+ docPresence.off('receive', handleReceive);
204
+ };
205
+ }
206
+ }, [docPresence]);
207
+
208
+ console.log(sidebarPresenceIndicators);
209
+
114
210
  return (
115
211
  <div
116
212
  className="vz-sidebar"
@@ -135,8 +231,9 @@ export const VZSidebar = ({
135
231
  }
136
232
  >
137
233
  <i
138
- onClick={() => setIsSearchOpen(false)}
234
+ id="files-icon"
139
235
  className="icon-button icon-button-dark"
236
+ onClick={() => setIsSearchOpen(false)}
140
237
  >
141
238
  <FolderSVG />
142
239
  </i>
@@ -151,8 +248,9 @@ export const VZSidebar = ({
151
248
  }
152
249
  >
153
250
  <i
154
- onClick={() => setIsSearchOpen(true)}
251
+ id="search-icon"
155
252
  className="icon-button icon-button-dark"
253
+ onClick={() => setIsSearchOpen(true)}
156
254
  >
157
255
  <SearchSVG />
158
256
  </i>
@@ -167,8 +265,9 @@ export const VZSidebar = ({
167
265
  }
168
266
  >
169
267
  <i
170
- onClick={handleQuestionMarkClick}
268
+ id="shortcut-icon"
171
269
  className="icon-button icon-button-dark"
270
+ onClick={handleQuestionMarkClick}
172
271
  >
173
272
  <QuestionMarkSVG />
174
273
  </i>
@@ -187,7 +286,10 @@ export const VZSidebar = ({
187
286
  target="_blank"
188
287
  rel="noopener noreferrer"
189
288
  >
190
- <i className="icon-button icon-button-dark">
289
+ <i
290
+ id="bug-icon"
291
+ className="icon-button icon-button-dark"
292
+ >
191
293
  <BugSVG />
192
294
  </i>
193
295
  </a>
@@ -202,8 +304,9 @@ export const VZSidebar = ({
202
304
  }
203
305
  >
204
306
  <i
205
- onClick={handleSettingsClick}
307
+ id="settings-icon"
206
308
  className="icon-button icon-button-dark"
309
+ onClick={handleSettingsClick}
207
310
  >
208
311
  <GearSVG />
209
312
  </i>
@@ -218,8 +321,9 @@ export const VZSidebar = ({
218
321
  }
219
322
  >
220
323
  <i
221
- onClick={handleOpenCreateFileModal}
324
+ id="new-file-icon"
222
325
  className="icon-button icon-button-dark"
326
+ onClick={handleOpenCreateFileModal}
223
327
  >
224
328
  <NewSVG />
225
329
  </i>
@@ -235,8 +339,9 @@ export const VZSidebar = ({
235
339
  }
236
340
  >
237
341
  <i
238
- onClick={handleOpenCreateDirModal}
342
+ id="new-directory-icon"
239
343
  className="icon-button icon-button-dark"
344
+ onClick={handleOpenCreateDirModal}
240
345
  >
241
346
  <FileSVG />
242
347
  </i>
@@ -248,18 +353,19 @@ export const VZSidebar = ({
248
353
  overlay={
249
354
  <Tooltip id="toggle-auto-follow">
250
355
  {enableAutoFollow
251
- ? 'Disable Auto Follow'
252
- : 'Enable Auto Follow'}
356
+ ? disableAutoFollowTooltipText
357
+ : enableAutoFollowTooltipText}
253
358
  </Tooltip>
254
359
  }
255
360
  >
256
361
  <i
257
- onClick={toggleAutoFollow}
362
+ id="auto-focus-icon"
258
363
  className={`icon-button icon-button-dark${
259
364
  enableAutoFollow
260
365
  ? ' vh-color-success-01'
261
366
  : ''
262
367
  }`}
368
+ onClick={toggleAutoFollow}
263
369
  >
264
370
  <PinSVG />
265
371
  </i>
@@ -253,4 +253,29 @@
253
253
  }
254
254
  }
255
255
  }
256
+ .name {
257
+ display: flex;
258
+ align-items: center;
259
+ white-space: nowrap;
260
+ overflow: hidden;
261
+ gap: 6px;
262
+ }
263
+
264
+ .presence-indicators {
265
+ display: flex;
266
+ gap: 4px;
267
+ margin-right: 6px; /* Space between the indicators and the file name */
268
+ }
269
+
270
+ .presence-indicator {
271
+ width: 16px;
272
+ height: 16px;
273
+ border-radius: 50%;
274
+ display: flex;
275
+ align-items: center;
276
+ justify-content: center;
277
+ font-size: 10px;
278
+ background-color: #ff5733;
279
+ color: #ffffff;
280
+ }
256
281
  }
@@ -1,5 +1,4 @@
1
- import { FileId, VZCodeContent } from '../types';
2
- import { TabState } from './vzReducer';
1
+ import { FileId, TabState, VZCodeContent } from '../types';
3
2
 
4
3
  // The delimiter used to separate file names in the `tabs` parameter.
5
4
  // We need a character that is both URL-safe (does not get escaped in URLs)
@@ -2,12 +2,14 @@ import { useCallback } from 'react';
2
2
  import { ThemeLabel } from './themes';
3
3
  import {
4
4
  FileId,
5
+ PresenceIndicator,
5
6
  SearchFileVisibility,
6
7
  ShareDBDoc,
8
+ TabState,
7
9
  Username,
8
10
  VZCodeContent,
9
11
  } from '../types';
10
- import { TabState, VZAction } from './vzReducer';
12
+ import { VZAction } from './vzReducer';
11
13
 
12
14
  // This is a custom hook that returns a set of functions
13
15
  // that can be used to dispatch actions to the reducer.
@@ -113,6 +115,13 @@ export const useActions = (
113
115
  [dispatch],
114
116
  );
115
117
 
118
+ // Toggle the focused variable, which should focus the search input
119
+ const toggleSearchFocused = useCallback(() => {
120
+ dispatch({
121
+ type: 'toggle_search_focused',
122
+ });
123
+ }, [dispatch]);
124
+
116
125
  // True to show the settings modal.
117
126
  const setIsSettingsOpen = useCallback(
118
127
  (value: boolean) => {
@@ -164,6 +173,22 @@ export const useActions = (
164
173
  });
165
174
  }, [dispatch]);
166
175
 
176
+ const updatePresenceIndicator = useCallback(
177
+ (presenceIndicator: PresenceIndicator) => {
178
+ dispatch({
179
+ type: 'update_presence_indicator',
180
+ presenceIndicator,
181
+ });
182
+ },
183
+ [dispatch],
184
+ );
185
+
186
+ const splitCurrentPane = useCallback(() => {
187
+ dispatch({
188
+ type: 'split_current_pane',
189
+ });
190
+ }, [dispatch]);
191
+
167
192
  return {
168
193
  setActiveFileId,
169
194
  setActiveFileLeft,
@@ -175,6 +200,7 @@ export const useActions = (
175
200
  setSearch,
176
201
  setSearchResults,
177
202
  setSearchFileVisibility,
203
+ toggleSearchFocused,
178
204
  setIsSettingsOpen,
179
205
  setIsDocOpen,
180
206
  closeSettings,
@@ -182,5 +208,7 @@ export const useActions = (
182
208
  editorNoLongerWantsFocus,
183
209
  setUsername,
184
210
  toggleAutoFollow,
211
+ updatePresenceIndicator,
212
+ splitCurrentPane,
185
213
  };
186
214
  };
@@ -1,4 +1,4 @@
1
- import { useEffect } from 'react';
1
+ import { useContext, useEffect } from 'react';
2
2
  import { shouldTriggerRun } from './shouldTriggerRun';
3
3
  import { syntaxTree } from '@codemirror/language';
4
4
  import { SyntaxNode, SyntaxNodeRef } from '@lezer/common';
@@ -151,6 +151,18 @@ function jumpToDefinition(
151
151
  }
152
152
  }
153
153
 
154
+ // Sidebar keyboard shortcuts in form Ctrl + Shift + <key>
155
+ const sideBarKeyBoardMap = {
156
+ E: 'files-icon',
157
+ F: 'search-icon',
158
+ K: 'shortcut-icon',
159
+ B: 'bug-icon',
160
+ S: 'settings-icon',
161
+ N: 'new-file-icon',
162
+ D: 'new-directory-icon',
163
+ A: 'auto-focus-icon',
164
+ };
165
+
154
166
  // This module implements the keyboard shortcuts
155
167
  // for the VZCode editor.
156
168
  // These include:
@@ -166,6 +178,7 @@ export const useKeyboardShortcuts = ({
166
178
  handleOpenCreateFileModal,
167
179
  setActiveFileLeft,
168
180
  setActiveFileRight,
181
+ toggleSearchFocused,
169
182
  runPrettierRef,
170
183
  runCodeRef,
171
184
  sidebarRef,
@@ -177,6 +190,7 @@ export const useKeyboardShortcuts = ({
177
190
  handleOpenCreateFileModal: () => void;
178
191
  setActiveFileLeft: () => void;
179
192
  setActiveFileRight: () => void;
193
+ toggleSearchFocused: () => void;
180
194
  runPrettierRef: React.MutableRefObject<() => void>;
181
195
  runCodeRef: React.MutableRefObject<() => void>;
182
196
  sidebarRef: React.RefObject<HTMLDivElement>;
@@ -203,6 +217,22 @@ export const useKeyboardShortcuts = ({
203
217
  return;
204
218
  }
205
219
 
220
+ if (event.ctrlKey && event.shiftKey) {
221
+ // Handle keyboard shortcuts related to the side bar icons
222
+ document
223
+ .getElementById(sideBarKeyBoardMap[event.key])
224
+ ?.click();
225
+
226
+ // Ensure the search input is always focused
227
+ if (event.key === 'F') {
228
+ toggleSearchFocused();
229
+ }
230
+ } else if (event.ctrlKey && event.key === ',') {
231
+ document
232
+ .getElementById(sideBarKeyBoardMap['S'])
233
+ ?.click();
234
+ }
235
+
206
236
  if (event.ctrlKey === true) {
207
237
  // On holding CTRL key, search for a potential definition jump using mouse location
208
238
  document.addEventListener(
@@ -1,6 +1,5 @@
1
1
  import { useSearchParams } from 'react-router-dom';
2
- import { FileId, VZCodeContent } from '../types';
3
- import { TabState } from './vzReducer';
2
+ import { FileId, TabState, VZCodeContent } from '../types';
4
3
  import { useEffect, useMemo, useRef } from 'react';
5
4
  import {
6
5
  TabStateParams,
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from 'vitest';
1
+ import { assert, describe, expect, it } from 'vitest';
2
2
  import { closeTabsReducer } from './closeTabsReducer';
3
3
  import { VZAction, VZState, createInitialState } from '.';
4
4
  import { defaultTheme } from '../themes';
@@ -25,8 +25,9 @@ describe('closeTabsReducer', () => {
25
25
  action,
26
26
  );
27
27
 
28
- expect(newState.tabList.length).toBe(0);
29
- expect(newState.activeFileId).toBeNull();
28
+ assert(newState.pane.type === 'leafPane');
29
+ expect(newState.pane.tabList.length).toBe(0);
30
+ expect(newState.pane.activeFileId).toBeNull();
30
31
  });
31
32
 
32
33
  it('Keeps other tabs open', () => {
@@ -36,11 +37,14 @@ describe('closeTabsReducer', () => {
36
37
  };
37
38
  const stateWithTabs = {
38
39
  ...initialState,
39
- tabList: [
40
- { fileId: 'file1', isTransient: false },
41
- { fileId: 'file2', isTransient: false },
42
- ],
43
- activeFileId: 'file1',
40
+ pane: {
41
+ ...initialState.pane,
42
+ tabList: [
43
+ { fileId: 'file1', isTransient: false },
44
+ { fileId: 'file2', isTransient: false },
45
+ ],
46
+ activeFileId: 'file1',
47
+ },
44
48
  };
45
49
 
46
50
  const newState = closeTabsReducer(
@@ -48,24 +52,29 @@ describe('closeTabsReducer', () => {
48
52
  action,
49
53
  );
50
54
 
51
- expect(newState.tabList.length).toBe(1);
52
- expect(newState.tabList[0].fileId).toBe('file1');
53
- expect(newState.activeFileId).toBe('file1');
55
+ assert(newState.pane.type === 'leafPane');
56
+ expect(newState.pane.tabList.length).toBe(1);
57
+ expect(newState.pane.tabList[0].fileId).toBe('file1');
58
+ expect(newState.pane.activeFileId).toBe('file1');
54
59
  });
55
60
 
56
- it('Activates previous tab after closing an active tab', () => {
61
+ it.only('Activates previous tab after closing an active tab', () => {
57
62
  const action: VZAction = {
58
63
  type: 'close_tabs',
59
64
  fileIdsToClose: ['file2'],
60
65
  };
61
- const stateWithTabs = {
66
+ const stateWithTabs: VZState = {
62
67
  ...initialState,
63
- tabList: [
64
- { fileId: 'file1', isTransient: false },
65
- { fileId: 'file2', isTransient: false },
66
- { fileId: 'file3', isTransient: false },
67
- ],
68
- activeFileId: 'file2',
68
+ pane: {
69
+ ...initialState.pane,
70
+ type: 'leafPane',
71
+ tabList: [
72
+ { fileId: 'file1', isTransient: false },
73
+ { fileId: 'file2', isTransient: false },
74
+ { fileId: 'file3', isTransient: false },
75
+ ],
76
+ activeFileId: 'file2',
77
+ },
69
78
  };
70
79
 
71
80
  const newState = closeTabsReducer(
@@ -73,8 +82,9 @@ describe('closeTabsReducer', () => {
73
82
  action,
74
83
  );
75
84
 
76
- expect(newState.tabList.length).toBe(2);
77
- expect(newState.activeFileId).toBe('file1'); // should switch to the previous tab
85
+ assert(newState.pane.type === 'leafPane');
86
+ expect(newState.pane.tabList.length).toBe(2);
87
+ expect(newState.pane.activeFileId).toBe('file1'); // should switch to the previous tab
78
88
  });
79
89
 
80
90
  it('Does not modify the state if closing non-existing tabs', () => {
@@ -84,12 +94,15 @@ describe('closeTabsReducer', () => {
84
94
  };
85
95
  const stateWithTabs = {
86
96
  ...initialState,
87
- tabList: [
88
- { fileId: 'file1', isTransient: false },
89
- { fileId: 'file2', isTransient: false },
90
- { fileId: 'file3', isTransient: false },
91
- ],
92
- activeFileId: 'file2',
97
+ pane: {
98
+ ...initialState.pane,
99
+ tabList: [
100
+ { fileId: 'file1', isTransient: false },
101
+ { fileId: 'file2', isTransient: false },
102
+ { fileId: 'file3', isTransient: false },
103
+ ],
104
+ activeFileId: 'file2',
105
+ },
93
106
  };
94
107
 
95
108
  const newState = closeTabsReducer(
@@ -121,8 +134,9 @@ describe('closeTabsReducer', () => {
121
134
  action,
122
135
  );
123
136
 
124
- expect(newState.tabList.length).toBe(2);
125
- expect(newState.activeFileId).toBe('file3'); // remains unchanged
137
+ assert(newState.pane.type === 'leafPane');
138
+ expect(newState.pane.tabList.length).toBe(2);
139
+ expect(newState.pane.activeFileId).toBe('file3'); // remains unchanged
126
140
  });
127
141
 
128
142
  it('Closes the first tab out of many, when it is active', () => {
@@ -146,8 +160,9 @@ describe('closeTabsReducer', () => {
146
160
  action,
147
161
  );
148
162
 
149
- expect(newState.tabList.length).toBe(2);
150
- expect(newState.activeFileId).toBe('file2'); // switches to the next available tab
163
+ assert(newState.pane.type === 'leafPane');
164
+ expect(newState.pane.tabList.length).toBe(2);
165
+ expect(newState.pane.activeFileId).toBe('file2'); // switches to the next available tab
151
166
  });
152
167
 
153
168
  it('Closes multiple tabs at once', () => {
@@ -171,7 +186,8 @@ describe('closeTabsReducer', () => {
171
186
  action,
172
187
  );
173
188
 
174
- expect(newState.tabList.length).toBe(1);
175
- expect(newState.activeFileId).toBe('file3'); // switches to the next available tab
189
+ assert(newState.pane.type === 'leafPane');
190
+ expect(newState.pane.tabList.length).toBe(1);
191
+ expect(newState.pane.activeFileId).toBe('file3'); // switches to the next available tab
176
192
  });
177
193
  });