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.
Files changed (35) hide show
  1. package/README.md +1 -1
  2. package/dist/assets/{index-DXNuoOqQ.js → index-4cYZN5Uf.js} +57 -57
  3. package/dist/assets/{index--bvX81xc.js → index-CvSVuMne.js} +57 -57
  4. package/dist/assets/worker-CJWrLBN2.js +286 -0
  5. package/dist/index.html +1 -1
  6. package/package.json +13 -12
  7. package/src/client/AIAssist/AIAssistCodeMirrorKeyMap.ts +5 -2
  8. package/src/client/AIAssist/startAIAssist.ts +1 -1
  9. package/src/client/CodeEditor/getOrCreateEditor.ts +4 -0
  10. package/src/client/CodeEditor/index.tsx +2 -0
  11. package/src/client/CodeEditor/json1PresenceDisplay.ts +128 -90
  12. package/src/client/TabList/Tab.tsx +1 -2
  13. package/src/client/TabList/index.tsx +1 -1
  14. package/src/client/VZCodeContext.tsx +17 -14
  15. package/src/client/VZKeyboardShortcutsDoc.tsx +2 -1
  16. package/src/client/VZSidebar/Search.tsx +7 -2
  17. package/src/client/VZSidebar/index.tsx +87 -24
  18. package/src/client/tabsSearchParameters.ts +1 -2
  19. package/src/client/useActions.ts +10 -1
  20. package/src/client/useKeyboardShortcuts.ts +31 -1
  21. package/src/client/useURLSync.ts +1 -2
  22. package/src/client/vzReducer/closeTabReducer.test.ts +49 -33
  23. package/src/client/vzReducer/closeTabsReducer.ts +51 -19
  24. package/src/client/vzReducer/createInitialState.ts +8 -3
  25. package/src/client/vzReducer/findPane.ts +19 -0
  26. package/src/client/vzReducer/index.ts +17 -25
  27. package/src/client/vzReducer/openTabReducer.test.ts +38 -21
  28. package/src/client/vzReducer/openTabReducer.ts +26 -10
  29. package/src/client/vzReducer/searchReducer.ts +21 -1
  30. package/src/client/vzReducer/setActiveFileIdReducer.ts +23 -4
  31. package/src/client/vzReducer/setActiveFileLeftRightReducer.ts +37 -8
  32. package/src/client/vzReducer/updatePane.ts +44 -0
  33. package/src/server/index.js +1 -0
  34. package/src/types.ts +69 -70
  35. package/dist/assets/worker-Cs0ZU3m1.js +0 -291
@@ -35,21 +35,74 @@ import './styles.scss';
35
35
  const enableConnectionStatus = true;
36
36
 
37
37
  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',
38
+ createFileTooltipText = (
39
+ <>
40
+ <strong>New file</strong>
41
+ {/* TODO verify that Ctrl + Shift + N works - does not work in Linux */}
42
+ <div>(Alt + N or Ctrl + Shift + N)</div>
43
+ </>
44
+ ),
45
+ createDirTooltipText = (
46
+ <div>
47
+ <strong>New Directory</strong>
48
+ {/* TODO consider Alt+D, as Ctrl + Shift + D does not work in Linux */}
49
+ <div>(Ctrl + Shift + D)</div>
50
+ </div>
51
+ ),
52
+ openSettingsTooltipText = (
53
+ <div>
54
+ <strong>Open Settings</strong>
55
+ <div>(Ctrl + Shift + S or Ctrl + ,)</div>
56
+ </div>
57
+ ),
58
+ openKeyboardShortcuts = (
59
+ <div>
60
+ <strong>Keyboard Shortcuts</strong>
61
+ <div>(Ctrl + Shift + K)</div>
62
+ </div>
63
+ ),
64
+ reportBugTooltipText = (
65
+ <div>
66
+ <strong>Report Bug</strong>
67
+ {/* TODO get this keyboard shortcut working? */}
68
+ {/* <div>(Ctrl + Shift + B)</div> */}
69
+ </div>
70
+ ),
71
+ searchToolTipText = (
72
+ <div>
73
+ <strong>Search</strong>
74
+ <div>(Ctrl + Shift + F)</div>
75
+ </div>
76
+ ),
77
+ filesToolTipText = (
78
+ <div>
79
+ <strong>Files</strong>
80
+ <div>(Ctrl + Shift + E)</div>
81
+ </div>
82
+ ),
83
+ enableAutoFollowTooltipText = (
84
+ <div>
85
+ <strong>Enable Auto Follow</strong>
86
+ <div>(Ctrl + Shift + A)</div>
87
+ </div>
88
+ ),
89
+ disableAutoFollowTooltipText = (
90
+ <div>
91
+ <strong>Disable Auto Follow</strong>
92
+ {/* TODO consider Alt+A, this breaks in Linux */}
93
+ <div>(Ctrl + Shift + A)</div>
94
+ </div>
95
+ ),
45
96
  }: {
46
- createFileTooltipText?: string;
47
- createDirTooltipText?: string;
48
- openSettingsTooltipText?: string;
49
- reportBugTooltipText?: string;
50
- openKeyboardShortcuts?: string;
51
- searchToolTipText?: string;
52
- filesToolTipText?: string;
97
+ createFileTooltipText?: React.ReactNode;
98
+ createDirTooltipText?: React.ReactNode;
99
+ openSettingsTooltipText?: React.ReactNode;
100
+ reportBugTooltipText?: React.ReactNode;
101
+ openKeyboardShortcuts?: React.ReactNode;
102
+ searchToolTipText?: React.ReactNode;
103
+ filesToolTipText?: React.ReactNode;
104
+ enableAutoFollowTooltipText?: React.ReactNode;
105
+ disableAutoFollowTooltipText?: React.ReactNode;
53
106
  }) => {
54
107
  const {
55
108
  files,
@@ -135,8 +188,9 @@ export const VZSidebar = ({
135
188
  }
136
189
  >
137
190
  <i
138
- onClick={() => setIsSearchOpen(false)}
191
+ id="files-icon"
139
192
  className="icon-button icon-button-dark"
193
+ onClick={() => setIsSearchOpen(false)}
140
194
  >
141
195
  <FolderSVG />
142
196
  </i>
@@ -151,8 +205,9 @@ export const VZSidebar = ({
151
205
  }
152
206
  >
153
207
  <i
154
- onClick={() => setIsSearchOpen(true)}
208
+ id="search-icon"
155
209
  className="icon-button icon-button-dark"
210
+ onClick={() => setIsSearchOpen(true)}
156
211
  >
157
212
  <SearchSVG />
158
213
  </i>
@@ -167,8 +222,9 @@ export const VZSidebar = ({
167
222
  }
168
223
  >
169
224
  <i
170
- onClick={handleQuestionMarkClick}
225
+ id="shortcut-icon"
171
226
  className="icon-button icon-button-dark"
227
+ onClick={handleQuestionMarkClick}
172
228
  >
173
229
  <QuestionMarkSVG />
174
230
  </i>
@@ -187,7 +243,10 @@ export const VZSidebar = ({
187
243
  target="_blank"
188
244
  rel="noopener noreferrer"
189
245
  >
190
- <i className="icon-button icon-button-dark">
246
+ <i
247
+ id="bug-icon"
248
+ className="icon-button icon-button-dark"
249
+ >
191
250
  <BugSVG />
192
251
  </i>
193
252
  </a>
@@ -202,8 +261,9 @@ export const VZSidebar = ({
202
261
  }
203
262
  >
204
263
  <i
205
- onClick={handleSettingsClick}
264
+ id="settings-icon"
206
265
  className="icon-button icon-button-dark"
266
+ onClick={handleSettingsClick}
207
267
  >
208
268
  <GearSVG />
209
269
  </i>
@@ -218,8 +278,9 @@ export const VZSidebar = ({
218
278
  }
219
279
  >
220
280
  <i
221
- onClick={handleOpenCreateFileModal}
281
+ id="new-file-icon"
222
282
  className="icon-button icon-button-dark"
283
+ onClick={handleOpenCreateFileModal}
223
284
  >
224
285
  <NewSVG />
225
286
  </i>
@@ -235,8 +296,9 @@ export const VZSidebar = ({
235
296
  }
236
297
  >
237
298
  <i
238
- onClick={handleOpenCreateDirModal}
299
+ id="new-directory-icon"
239
300
  className="icon-button icon-button-dark"
301
+ onClick={handleOpenCreateDirModal}
240
302
  >
241
303
  <FileSVG />
242
304
  </i>
@@ -248,18 +310,19 @@ export const VZSidebar = ({
248
310
  overlay={
249
311
  <Tooltip id="toggle-auto-follow">
250
312
  {enableAutoFollow
251
- ? 'Disable Auto Follow'
252
- : 'Enable Auto Follow'}
313
+ ? disableAutoFollowTooltipText
314
+ : enableAutoFollowTooltipText}
253
315
  </Tooltip>
254
316
  }
255
317
  >
256
318
  <i
257
- onClick={toggleAutoFollow}
319
+ id="auto-focus-icon"
258
320
  className={`icon-button icon-button-dark${
259
321
  enableAutoFollow
260
322
  ? ' vh-color-success-01'
261
323
  : ''
262
324
  }`}
325
+ onClick={toggleAutoFollow}
263
326
  >
264
327
  <PinSVG />
265
328
  </i>
@@ -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)
@@ -4,10 +4,11 @@ import {
4
4
  FileId,
5
5
  SearchFileVisibility,
6
6
  ShareDBDoc,
7
+ TabState,
7
8
  Username,
8
9
  VZCodeContent,
9
10
  } from '../types';
10
- import { TabState, VZAction } from './vzReducer';
11
+ import { VZAction } from './vzReducer';
11
12
 
12
13
  // This is a custom hook that returns a set of functions
13
14
  // that can be used to dispatch actions to the reducer.
@@ -113,6 +114,13 @@ export const useActions = (
113
114
  [dispatch],
114
115
  );
115
116
 
117
+ // Toggle the focused variable, which should focus the search input
118
+ const toggleSearchFocused = useCallback(() => {
119
+ dispatch({
120
+ type: 'toggle_search_focused',
121
+ });
122
+ }, [dispatch]);
123
+
116
124
  // True to show the settings modal.
117
125
  const setIsSettingsOpen = useCallback(
118
126
  (value: boolean) => {
@@ -175,6 +183,7 @@ export const useActions = (
175
183
  setSearch,
176
184
  setSearchResults,
177
185
  setSearchFileVisibility,
186
+ toggleSearchFocused,
178
187
  setIsSettingsOpen,
179
188
  setIsDocOpen,
180
189
  closeSettings,
@@ -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
  });
@@ -1,5 +1,12 @@
1
- import { TabState, VZAction, VZState } from '.';
2
- import { FileId } from '../../types';
1
+ import { VZAction, VZState } from '.';
2
+ import {
3
+ FileId,
4
+ Pane,
5
+ PaneId,
6
+ TabState,
7
+ } from '../../types';
8
+ import { findPane } from './findPane';
9
+ import { updatePane } from './updatePane';
3
10
 
4
11
  export const closeTabsReducer = (
5
12
  state: VZState,
@@ -9,35 +16,56 @@ export const closeTabsReducer = (
9
16
  return state;
10
17
  }
11
18
 
12
- const newTabList: Array<TabState> = state.tabList.filter(
13
- (tabState) =>
14
- !action.fileIdsToClose.includes(tabState.fileId),
15
- );
19
+ const { pane, activePaneId } = state;
20
+ const activePane: Pane = findPane(pane, activePaneId);
21
+
22
+ if (activePane.type !== 'leafPane') {
23
+ throw new Error(
24
+ 'closeTabsReducer: activePane is not a leafPane',
25
+ );
26
+ }
27
+
28
+ // Derive the new tab list by filtering out the tabs to close.
29
+ const newTabList: Array<TabState> =
30
+ activePane.tabList.filter(
31
+ (tabState) =>
32
+ !action.fileIdsToClose.includes(tabState.fileId),
33
+ );
16
34
 
17
35
  // If the active tab wasn't closed, keep it active.
18
36
  if (
19
- !action.fileIdsToClose.includes(state.activeFileId!)
37
+ !action.fileIdsToClose.includes(
38
+ activePane.activeFileId!,
39
+ )
20
40
  ) {
21
41
  return {
22
42
  ...state,
23
- tabList: newTabList,
43
+ pane: updatePane({
44
+ pane,
45
+ activePaneId,
46
+ newTabList,
47
+ }),
24
48
  };
25
49
  }
26
50
 
27
- const originalIndex = state.tabList.findIndex(
28
- (tabState) => tabState.fileId === state.activeFileId,
29
- );
30
-
51
+ // Otherwise, we'll need to also find a new active tab.
31
52
  let newActiveFileId: FileId | null = null;
32
53
 
54
+ // The question becomes: which tab should be active now?
55
+ // - Either the tab to the left or right of the closed tab.
56
+
33
57
  // Try getting the previous available tab first.
58
+ const originalIndex = activePane.tabList.findIndex(
59
+ (tabState) =>
60
+ tabState.fileId === activePane.activeFileId,
61
+ );
34
62
  for (let i = originalIndex - 1; i >= 0; i--) {
35
63
  if (
36
64
  !action.fileIdsToClose.includes(
37
- state.tabList[i].fileId,
65
+ activePane.tabList[i].fileId,
38
66
  )
39
67
  ) {
40
- newActiveFileId = state.tabList[i].fileId;
68
+ newActiveFileId = activePane.tabList[i].fileId;
41
69
  break;
42
70
  }
43
71
  }
@@ -46,15 +74,15 @@ export const closeTabsReducer = (
46
74
  if (!newActiveFileId) {
47
75
  for (
48
76
  let i = originalIndex + 1;
49
- i < state.tabList.length;
77
+ i < activePane.tabList.length;
50
78
  i++
51
79
  ) {
52
80
  if (
53
81
  !action.fileIdsToClose.includes(
54
- state.tabList[i].fileId,
82
+ activePane.tabList[i].fileId,
55
83
  )
56
84
  ) {
57
- newActiveFileId = state.tabList[i].fileId;
85
+ newActiveFileId = activePane.tabList[i].fileId;
58
86
  break;
59
87
  }
60
88
  }
@@ -62,7 +90,11 @@ export const closeTabsReducer = (
62
90
 
63
91
  return {
64
92
  ...state,
65
- tabList: newTabList,
66
- activeFileId: newActiveFileId,
93
+ pane: updatePane({
94
+ pane,
95
+ activePaneId,
96
+ newTabList,
97
+ newActiveFileId,
98
+ }),
67
99
  };
68
100
  };
@@ -9,10 +9,15 @@ export const createInitialState = ({
9
9
  defaultTheme: ThemeLabel;
10
10
  initialUsername?: Username;
11
11
  }): VZState => ({
12
- tabList: [],
13
- activeFileId: null,
12
+ pane: {
13
+ id: 'root',
14
+ type: 'leafPane',
15
+ tabList: [],
16
+ activeFileId: null,
17
+ },
18
+ activePaneId: 'root',
14
19
  theme: defaultTheme,
15
- search: { pattern: '', results: {} },
20
+ search: { pattern: '', results: {}, focused: false },
16
21
  isSearchOpen: false,
17
22
  isSettingsOpen: false,
18
23
  isDocOpen: false,
@@ -0,0 +1,19 @@
1
+ import { Pane, PaneId } from '../../types';
2
+
3
+ // Recursively finds a pane by id, given a root pane node.
4
+ export const findPane = (
5
+ pane: Pane,
6
+ paneId: PaneId,
7
+ ): Pane => {
8
+ if (pane.id === paneId) {
9
+ return pane;
10
+ }
11
+ if (pane.type === 'splitPane') {
12
+ for (const child of pane.children) {
13
+ const foundPane = findPane(child, paneId);
14
+ if (foundPane) {
15
+ return foundPane;
16
+ }
17
+ }
18
+ }
19
+ };