@electerm/electerm-react 1.60.56 → 1.70.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 (40) hide show
  1. package/client/common/constants.js +1 -0
  2. package/client/common/default-log-path.js +5 -0
  3. package/client/common/default-setting.js +3 -1
  4. package/client/common/find-bookmark-group-id.js +1 -2
  5. package/client/components/batch-op/batch-op-entry.jsx +13 -0
  6. package/client/components/bookmark-form/index.jsx +1 -1
  7. package/client/components/footer/footer-entry.jsx +9 -16
  8. package/client/components/icons/split-view.jsx +14 -0
  9. package/client/components/main/main.jsx +9 -10
  10. package/client/components/session/session.jsx +285 -70
  11. package/client/components/session/session.styl +2 -0
  12. package/client/components/setting-panel/on-tree-drop.js +10 -19
  13. package/client/components/setting-panel/setting-terminal.jsx +94 -20
  14. package/client/components/setting-panel/tab-settings.jsx +2 -1
  15. package/client/components/setting-sync/server-data-status.jsx +81 -0
  16. package/client/components/setting-sync/setting-sync-form.jsx +6 -0
  17. package/client/components/setting-sync/setting-sync.jsx +8 -5
  18. package/client/components/sftp/list-table-ui.jsx +13 -15
  19. package/client/components/sftp/sftp-entry.jsx +4 -22
  20. package/client/components/shortcuts/shortcut-control.jsx +10 -1
  21. package/client/components/tabs/tab.jsx +7 -8
  22. package/client/components/tabs/tabs.styl +3 -0
  23. package/client/components/terminal/term-search.jsx +2 -1
  24. package/client/components/terminal/terminal.jsx +26 -8
  25. package/client/components/terminal-info/base.jsx +9 -4
  26. package/client/components/tree-list/bookmark-toolbar.jsx +2 -3
  27. package/client/components/tree-list/tree-list.jsx +4 -7
  28. package/client/components/tree-list/tree-search.jsx +1 -0
  29. package/client/store/bookmark-group.js +1 -2
  30. package/client/store/init-state.js +3 -0
  31. package/client/store/item.js +1 -2
  32. package/client/store/load-data.js +1 -1
  33. package/client/store/setting.js +1 -2
  34. package/client/store/store.js +13 -15
  35. package/client/store/sync.js +42 -7
  36. package/client/store/terminal-theme.js +4 -4
  37. package/client/store/ui-theme.js +3 -10
  38. package/client/store/watch.js +6 -0
  39. package/package.json +1 -1
  40. package/client/components/main/loading.jsx +0 -25
@@ -5,7 +5,7 @@
5
5
  import {
6
6
  defaultBookmarkGroupId
7
7
  } from '../../common/constants'
8
- import { isEqual, find, last, remove } from 'lodash-es'
8
+ import { isEqual, last, remove } from 'lodash-es'
9
9
  import copy from 'json-deep-copy'
10
10
  import { action } from 'manate'
11
11
 
@@ -25,19 +25,15 @@ export default action((info, props) => {
25
25
  const isSameLevel = fromPosesLevel.length === toPosesLevel.length
26
26
  const isSameCat = isEqual(fromPosesLevel, toPosesLevel) && dropToGap
27
27
  const { bookmarks, bookmarkGroups } = window.store
28
- let from = find(
29
- bookmarks,
28
+ let from = bookmarks.find(
30
29
  d => d.id === fromId
31
- ) || find(
32
- bookmarkGroups,
30
+ ) || bookmarkGroups.find(
33
31
  d => d.id === fromId
34
32
  )
35
33
  const fromLeaf = !!from && !from.bookmarkIds
36
- let to = find(
37
- bookmarks,
34
+ let to = bookmarks.find(
38
35
  d => d.id === toId
39
- ) || find(
40
- bookmarkGroups,
36
+ ) || bookmarkGroups.find(
41
37
  d => d.id === toId
42
38
  )
43
39
  const toLeaf = !!to && !to.bookmarkIds
@@ -87,12 +83,10 @@ export default action((info, props) => {
87
83
  let fromGroup = null
88
84
  if (fromPoses.length > 2) {
89
85
  fromGroup = fromLeaf
90
- ? find(
91
- bookmarkGroups,
86
+ ? bookmarkGroups.find(
92
87
  d => (d.bookmarkIds || []).includes(fromId)
93
88
  )
94
- : find(
95
- bookmarkGroups,
89
+ : bookmarkGroups.find(
96
90
  d => (d.bookmarkGroupIds || []).includes(fromId)
97
91
  )
98
92
  }
@@ -100,8 +94,7 @@ export default action((info, props) => {
100
94
  const toFirstLevel = toPoses.length === 2 && dropToGap
101
95
  if (!toFirstLevel) {
102
96
  toGroup = dropToGap
103
- ? find(
104
- bookmarkGroups,
97
+ ? bookmarkGroups.find(
105
98
  d => {
106
99
  const arr = toLeaf
107
100
  ? d.bookmarkIds
@@ -111,12 +104,10 @@ export default action((info, props) => {
111
104
  )
112
105
  : (
113
106
  toLeaf
114
- ? find(
115
- bookmarkGroups,
107
+ ? bookmarkGroups.find(
116
108
  d => (d.bookmarkIds || []).includes(toId)
117
109
  )
118
- : find(
119
- bookmarkGroups,
110
+ : bookmarkGroups.find(
120
111
  d => d.id === toId
121
112
  )
122
113
  )
@@ -12,7 +12,8 @@ import {
12
12
  InputNumber,
13
13
  Button,
14
14
  AutoComplete,
15
- Tooltip
15
+ Tooltip,
16
+ Flex
16
17
  } from 'antd'
17
18
  import deepCopy from 'json-deep-copy'
18
19
  import {
@@ -24,12 +25,16 @@ import {
24
25
  import defaultSettings from '../../common/default-setting'
25
26
  import ShowItem from '../common/show-item'
26
27
  import { osResolve } from '../../common/resolve'
28
+ import { chooseSaveDirectory } from '../../common/choose-save-folder'
27
29
  import { isNumber, isNaN } from 'lodash-es'
28
30
  import mapper from '../../common/auto-complete-data-mapper'
29
31
  import KeywordForm from './keywords-form'
30
32
  import Link from '../common/external-link'
31
33
  import HelpIcon from '../common/help-icon'
32
34
  import KeywordsTransport from './keywords-transport'
35
+ import fs from '../../common/fs'
36
+ import uid from '../../common/uid'
37
+ import createDefaultSessionLogPath from '../../common/default-log-path'
33
38
  import './setting.styl'
34
39
 
35
40
  const { Option } = Select
@@ -111,18 +116,87 @@ export default class SettingTerminal extends Component {
111
116
  return this.saveConfig(data)
112
117
  }
113
118
 
114
- renderToggle = (name, extra = null) => {
119
+ renderToggle = (name, cls = 'pd2b') => {
115
120
  const checked = !!this.props.config[name]
116
121
  const txt = e(name)
117
122
  return (
118
- <div className='pd2b' key={'rt' + name}>
123
+ <div className={cls} key={'rt' + name}>
119
124
  <Switch
120
125
  checked={checked}
121
126
  checkedChildren={txt}
122
127
  unCheckedChildren={txt}
123
128
  onChange={v => this.onChangeValue(v, name)}
124
129
  />
125
- {isNumber(extra) ? null : extra}
130
+ </div>
131
+ )
132
+ }
133
+
134
+ testFolderPathCanSaveLog = async (path) => {
135
+ try {
136
+ const st = await fs.statCustom(path)
137
+ if (!st.isD) {
138
+ message.error('invalid log folder')
139
+ return false
140
+ }
141
+ const testFile = osResolve(path, uid + '.test.log')
142
+ await fs.touch(testFile)
143
+ await fs.unlink(testFile)
144
+ return true
145
+ } catch (err) {
146
+ message.error('invalid log folder')
147
+ return false
148
+ }
149
+ }
150
+
151
+ handleLogChange = (v) => {
152
+ if (v && !this.testFolderPathCanSaveLog(v)) {
153
+ return
154
+ }
155
+ this.onChangeValue(v, 'sessionLogPath')
156
+ }
157
+
158
+ handleChooseFolder = async () => {
159
+ const path = await chooseSaveDirectory()
160
+ if (path) {
161
+ this.handleLogChange(path)
162
+ }
163
+ }
164
+
165
+ renderLogPathControl = () => {
166
+ const { config } = this.props
167
+ const { sessionLogPath } = config
168
+ const path = sessionLogPath || createDefaultSessionLogPath()
169
+ const inputProps = {
170
+ value: sessionLogPath,
171
+ placeholder: path,
172
+ onChange: (e) => this.handleLogChange(e.target.value),
173
+ addonAfter: (
174
+ <>
175
+ <Button
176
+ onClick={this.handleChooseFolder}
177
+ className='mg1r'
178
+ size='small'
179
+ >
180
+ {e('chooseFolder')}
181
+ </Button>
182
+ <Button
183
+ size='small'
184
+ onClick={() => this.handleLogChange('')}
185
+ >
186
+ {e('reset')}
187
+ </Button>
188
+ </>
189
+ ),
190
+ addonBefore: (
191
+ <>
192
+ <span className='mg1r'>{e('terminalLogPath')}</span>
193
+ <ShowItem to={path} />
194
+ </>
195
+ )
196
+ }
197
+ return (
198
+ <div className='pd2b'>
199
+ <Input {...inputProps} />
126
200
  </div>
127
201
  )
128
202
  }
@@ -346,7 +420,7 @@ export default class SettingTerminal extends Component {
346
420
  }
347
421
  }
348
422
  return (
349
- <div className='pd1b'>
423
+ <div className='pd2b'>
350
424
  <span className='inline-title mg1r'>{e('cursorStyle')}</span>
351
425
  <Select
352
426
  {...props}
@@ -371,9 +445,9 @@ export default class SettingTerminal extends Component {
371
445
  const { fontFamily } = this.props.config
372
446
  const props = {
373
447
  mode: 'multiple',
374
- className: 'font-sel',
375
448
  onChange: this.handleChangeFont,
376
- value: fontFamily.split(/, */g).filter(d => d.trim())
449
+ value: fontFamily.split(/, */g).filter(d => d.trim()),
450
+ style: { width: '100%' }
377
451
  }
378
452
  return (
379
453
  <Select
@@ -415,7 +489,6 @@ export default class SettingTerminal extends Component {
415
489
  keywords = [{ color: 'red' }]
416
490
  } = this.props.config
417
491
  const {
418
- appPath,
419
492
  getThemeConfig
420
493
  } = this.props.store
421
494
  const ps = {
@@ -426,9 +499,6 @@ export default class SettingTerminal extends Component {
426
499
  submit: this.handleSubmitKeywords,
427
500
  themeConfig: getThemeConfig()
428
501
  }
429
- const terminalLogPath = appPath
430
- ? osResolve(appPath, 'electerm', 'session_logs')
431
- : window.et.sessionLogPath
432
502
  const tip = (
433
503
  <div>
434
504
  <span className='mg1r'>{e('supportRegexp')}</span>
@@ -470,10 +540,10 @@ export default class SettingTerminal extends Component {
470
540
  }, `${e('default')} ${e('fontSize')}`, 400)
471
541
  }
472
542
  <div className='pd2b'>
473
- <span className='inline-title mg1r'>{e('default')} {e('fontFamily')}</span>
474
- {
475
- this.renderFontFamily()
476
- }
543
+ <Flex align='center' gap='middle'>
544
+ <Flex><div className='inline-title'>{e('default')} {e('fontFamily')}</div></Flex>
545
+ <Flex flex='auto'>{this.renderFontFamily()}</Flex>
546
+ </Flex>
477
547
  </div>
478
548
  <div className='pd2b'>
479
549
  <div className='pd1b'>
@@ -509,9 +579,12 @@ export default class SettingTerminal extends Component {
509
579
  {
510
580
  this.renderCursorStyleSelect()
511
581
  }
512
- {this.renderToggle('saveTerminalLogToFile', (
513
- <ShowItem to={terminalLogPath} className='mg1l'>{e('open')}</ShowItem>
514
- ))}
582
+ {
583
+ this.renderLogPathControl()
584
+ }
585
+ {
586
+ this.renderToggle('saveTerminalLogToFile')
587
+ }
515
588
  {this.renderToggle('addTimeStampToTermLog')}
516
589
  {
517
590
  [
@@ -520,8 +593,9 @@ export default class SettingTerminal extends Component {
520
593
  'pasteWhenContextMenu',
521
594
  'copyWhenSelect',
522
595
  'ctrlOrMetaOpenTerminalLink',
523
- 'sftpPathFollowSsh'
524
- ].map(this.renderToggle)
596
+ 'sftpPathFollowSsh',
597
+ 'sshSftpSplitView'
598
+ ].map(d => this.renderToggle(d))
525
599
  }
526
600
  <div className='pd1b'>{e('terminalBackSpaceMode')}</div>
527
601
  <Select
@@ -33,7 +33,8 @@ export default auto(function TabSettings (props) {
33
33
  'isSyncingSetting',
34
34
  'isSyncDownload',
35
35
  'isSyncUpload',
36
- 'syncType'
36
+ 'syncType',
37
+ 'syncServerStatus'
37
38
  ])
38
39
  elem = <SyncSetting {...syncProps} />
39
40
  } else if (sid === settingTerminalId) {
@@ -0,0 +1,81 @@
1
+ import { useState } from 'react'
2
+ import { LoadingOutlined, ReloadOutlined } from '@ant-design/icons'
3
+ import dayjs from 'dayjs'
4
+
5
+ const e = window.translate
6
+
7
+ export default function ServerDataStatus (props) {
8
+ const { store } = window
9
+ const { type, status } = props
10
+ const [loading, setLoading] = useState(false)
11
+ const token = store.getSyncToken(type)
12
+ const gistId = store.getSyncGistId(type)
13
+ const canSync = token && (gistId || type === 'custom' || type === 'cloud')
14
+
15
+ async function handleReload () {
16
+ setLoading(true)
17
+ await store.previewServerData(type)
18
+ .catch(store.onError)
19
+ setLoading(false)
20
+ }
21
+
22
+ function renderReloadButton () {
23
+ if (loading) {
24
+ return (
25
+ <LoadingOutlined className='mg1l' />
26
+ )
27
+ }
28
+ return (
29
+ <ReloadOutlined
30
+ className='pointer mg1l hover-black'
31
+ onClick={handleReload}
32
+ />
33
+ )
34
+ }
35
+
36
+ function renderNoCredentials () {
37
+ return (
38
+ <p>
39
+ <span>{e('syncServerDataStatus')}: -</span>
40
+ </p>
41
+ )
42
+ }
43
+
44
+ function renderNoData () {
45
+ return (
46
+ <p>
47
+ <span>{e('syncServerDataStatus')}: -</span>
48
+ {renderReloadButton()}
49
+ </p>
50
+ )
51
+ }
52
+
53
+ function renderStatus () {
54
+ const {
55
+ lastSyncTime,
56
+ electermVersion,
57
+ deviceName = 'unknown'
58
+ } = status
59
+
60
+ return (
61
+ <p>
62
+ <span className='mg1r'>{e('syncServerDataStatus')}:</span>
63
+ <b className='mg1r'>{dayjs(lastSyncTime).format('YYYY-MM-DD HH:mm:ss')}</b>
64
+ <span className='mg1r'>{e('from')}:</span>
65
+ <b className='mg1r'>{deviceName}</b>
66
+ <b className='mg1r'>(v{electermVersion})</b>
67
+ {renderReloadButton()}
68
+ </p>
69
+ )
70
+ }
71
+
72
+ if (!canSync) {
73
+ return renderNoCredentials()
74
+ }
75
+
76
+ if (!status) {
77
+ return renderNoData()
78
+ }
79
+
80
+ return renderStatus()
81
+ }
@@ -14,6 +14,7 @@ import eq from 'fast-deep-equal'
14
14
  import { syncTokenCreateUrls, syncTypes } from '../../common/constants'
15
15
  import './sync.styl'
16
16
  import HelpIcon from '../common/help-icon'
17
+ import ServerDataStatus from './server-data-status'
17
18
 
18
19
  const FormItem = Form.Item
19
20
  const e = window.translate
@@ -207,6 +208,10 @@ export default function SyncForm (props) {
207
208
  </FormItem>
208
209
  )
209
210
  }
211
+ const sprops = {
212
+ type: syncType,
213
+ status: props.serverStatus
214
+ }
210
215
  return (
211
216
  <Form
212
217
  onFinish={save}
@@ -280,6 +285,7 @@ export default function SyncForm (props) {
280
285
  >{e('clear')}
281
286
  </Button>
282
287
  </p>
288
+ <ServerDataStatus {...sprops} />
283
289
  <p>
284
290
  {e('lastSyncTime')}: {timeFormatted}
285
291
  </p>
@@ -8,8 +8,10 @@ import { syncTypes, syncDataMaps } from '../../common/constants'
8
8
  import DataTransport from './data-import'
9
9
  import DataSelect from './data-select'
10
10
  import { pick } from 'lodash-es'
11
+ import { auto } from 'manate/react'
12
+ import deepCopy from 'json-deep-copy'
11
13
 
12
- export default function SyncSettingEntry (props) {
14
+ export default auto(function SyncSettingEntry (props) {
13
15
  const handleChange = (key) => {
14
16
  window.store.syncType = key
15
17
  }
@@ -29,8 +31,10 @@ export default function SyncSettingEntry (props) {
29
31
  'isSyncingSetting',
30
32
  'isSyncDownload',
31
33
  'isSyncUpload',
32
- 'syncType'
33
- ])
34
+ 'syncType',
35
+ 'serverStatus'
36
+ ]),
37
+ serverStatus: deepCopy(store.syncServerStatus[props.syncType])
34
38
  }
35
39
  const type = props.syncType
36
40
  const formData = {
@@ -44,7 +48,6 @@ export default function SyncSettingEntry (props) {
44
48
  return (
45
49
  <SyncForm
46
50
  {...syncProps}
47
- syncType={type}
48
51
  encrypt={syncSetting.syncEncrypt}
49
52
  formData={formData}
50
53
  />
@@ -86,4 +89,4 @@ export default function SyncSettingEntry (props) {
86
89
  </Spin>
87
90
  </div>
88
91
  )
89
- }
92
+ })
@@ -4,10 +4,6 @@
4
4
 
5
5
  import { Component } from 'react'
6
6
  import classnames from 'classnames'
7
- import { find } from 'lodash-es'
8
- import {
9
- sftpControlHeight
10
- } from '../../common/constants'
11
7
  import FileSection from './file-item'
12
8
  import PagedList from './paged-list'
13
9
  import FileListTableHeader from './file-table-header'
@@ -125,8 +121,7 @@ export default class FileListTable extends Component {
125
121
  onClickName = (e) => {
126
122
  const id = e.target.getAttribute('data-id')
127
123
  const { properties } = this.state
128
- const propObj = find(
129
- properties,
124
+ const propObj = properties.find(
130
125
  p => p.id === id
131
126
  )
132
127
  if (!propObj) {
@@ -280,11 +275,12 @@ export default class FileListTable extends Component {
280
275
 
281
276
  render () {
282
277
  const { fileList, height, type } = this.props
283
- const tableHeaderHeight = 30
278
+ // const tableHeaderHeight = 30
279
+ // const sh = sshSftpSplitView ? 0 : 32
284
280
  const props = {
285
281
  className: 'sftp-table-content overscroll-y relative',
286
282
  style: {
287
- height: height - sftpControlHeight - tableHeaderHeight
283
+ height: height - 42 - 30 - 32 - 90
288
284
  },
289
285
  draggable: false
290
286
  }
@@ -302,13 +298,15 @@ export default class FileListTable extends Component {
302
298
  <div
303
299
  {...props}
304
300
  >
305
- {this.props.renderEmptyFile(type)}
306
- {this.renderParent(type)}
307
- <PagedList
308
- list={fileList}
309
- renderItem={this.renderItem}
310
- hasPager={hasPager}
311
- />
301
+ <div>
302
+ {this.props.renderEmptyFile(type)}
303
+ {this.renderParent(type)}
304
+ <PagedList
305
+ list={fileList}
306
+ renderItem={this.renderItem}
307
+ hasPager={hasPager}
308
+ />
309
+ </div>
312
310
  </div>
313
311
  </div>
314
312
  )
@@ -55,20 +55,6 @@ export default class Sftp extends Component {
55
55
  }
56
56
 
57
57
  componentDidUpdate (prevProps, prevState) {
58
- if (
59
- (
60
- prevProps.enableSftp !== false &&
61
- !prevProps.pid &&
62
- this.props.pid
63
- ) ||
64
- (
65
- prevProps.pid &&
66
- prevProps.enableSftp === false &&
67
- this.props.enableSftp !== false
68
- )
69
- ) {
70
- this.initData(true)
71
- }
72
58
  if (
73
59
  this.props.config.autoRefreshWhenSwitchToSftp &&
74
60
  prevProps.pane !== this.props.pane &&
@@ -183,8 +169,9 @@ export default class Sftp extends Component {
183
169
  }, isEqual)
184
170
 
185
171
  isActive () {
186
- return this.props.enableSftp && this.props.currentBatchTabId === this.props.tab.id &&
187
- this.props.pane === paneMap.fileManager
172
+ return this.props.currentBatchTabId === this.props.tab.id &&
173
+ (this.props.pane === paneMap.fileManager ||
174
+ this.props.pane === paneMap.sftp || this.props.sshSftpSplitView)
188
175
  }
189
176
 
190
177
  getCwdLocal = () => {
@@ -407,7 +394,7 @@ export default class Sftp extends Component {
407
394
  this[type + 'Dom'].onPaste()
408
395
  }
409
396
 
410
- initData = async () => {
397
+ initData = () => {
411
398
  if (this.shouldRenderRemote()) {
412
399
  this.initRemoteAll()
413
400
  }
@@ -437,11 +424,6 @@ export default class Sftp extends Component {
437
424
  window.store.addTransferList(list)
438
425
  }
439
426
 
440
- computeListHeight = () => {
441
- const hasTransports = this.state.transports.length
442
- return this.props.height - 15 - (hasTransports ? 300 : 0)
443
- }
444
-
445
427
  onError = e => {
446
428
  window.store.onError(e)
447
429
  this.setState({
@@ -13,6 +13,15 @@ import { refs, refsStatic } from '../common/ref'
13
13
  import keyControlPressed from '../../common/key-control-pressed'
14
14
  import keyPressed from '../../common/key-pressed'
15
15
 
16
+ function isInputActive () {
17
+ const activeElement = document.activeElement
18
+ return activeElement && (
19
+ activeElement.tagName === 'INPUT' ||
20
+ activeElement.tagName === 'TEXTAREA' ||
21
+ activeElement.isContentEditable
22
+ )
23
+ }
24
+
16
25
  class ShortcutControl extends React.PureComponent {
17
26
  componentDidMount () {
18
27
  const onEvent = this.handleKeyboardEvent.bind(this)
@@ -39,7 +48,7 @@ class ShortcutControl extends React.PureComponent {
39
48
  // SFTP shortcuts handler
40
49
  handleSftpKeyboardEvent = (e) => {
41
50
  const activeSftp = this.getActiveSftp()
42
- if (!activeSftp || activeSftp.state.onDelete) {
51
+ if (!activeSftp || activeSftp.state.onDelete || isInputActive()) {
43
52
  return
44
53
  }
45
54
 
@@ -30,18 +30,17 @@ const onDragCls = 'ondrag-tab'
30
30
  const onDragOverCls = 'dragover-tab'
31
31
 
32
32
  class Tab extends Component {
33
- state = {
34
- terminalOnData: false
35
- }
36
-
37
- tabRef = createRef()
38
-
39
- componentDidMount () {
33
+ constructor (props) {
34
+ super(props)
35
+ this.state = {
36
+ terminalOnData: false
37
+ }
40
38
  this.id = 'tab-' + this.props.tab.id
41
39
  refs.add(this.id, this)
42
- window.addEventListener('message', this.onEvent)
43
40
  }
44
41
 
42
+ tabRef = createRef()
43
+
45
44
  componentDidUpdate (prevProps) {
46
45
  if (this.props.openContextMenu && !prevProps.openContextMenu) {
47
46
  this.handleContextMenu()
@@ -52,6 +52,8 @@
52
52
  &.active
53
53
  color text
54
54
  background main
55
+ .tab-count
56
+ opacity 1
55
57
  &:hover
56
58
  .tab-close
57
59
  display block
@@ -229,6 +231,7 @@
229
231
  padding 0 4px
230
232
  height 20px
231
233
  line-height 21px
234
+ opacity 0.8
232
235
  .no-session-history
233
236
  position absolute
234
237
  top 280px
@@ -191,7 +191,8 @@ export default class TermSearch extends PureComponent {
191
191
  if (
192
192
  !termSearchOpen ||
193
193
  !currentTab ||
194
- currentTab.pane === paneMap.fileManager
194
+ currentTab.pane === paneMap.fileManager ||
195
+ currentTab.pane === paneMap.sftp
195
196
  ) {
196
197
  return null
197
198
  }