@electerm/electerm-react 1.60.50 → 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 (44) 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 +4 -2
  4. package/client/common/download-mirrors.js +0 -4
  5. package/client/common/find-bookmark-group-id.js +1 -2
  6. package/client/components/ai/ai-config.jsx +2 -2
  7. package/client/components/batch-op/batch-op-entry.jsx +13 -0
  8. package/client/components/bookmark-form/index.jsx +1 -1
  9. package/client/components/footer/footer-entry.jsx +9 -16
  10. package/client/components/icons/split-view.jsx +14 -0
  11. package/client/components/main/main.jsx +9 -10
  12. package/client/components/session/session.jsx +285 -70
  13. package/client/components/session/session.styl +2 -0
  14. package/client/components/setting-panel/on-tree-drop.js +10 -19
  15. package/client/components/setting-panel/setting-modal.jsx +1 -0
  16. package/client/components/setting-panel/setting-terminal.jsx +94 -20
  17. package/client/components/setting-panel/tab-settings.jsx +2 -1
  18. package/client/components/setting-sync/server-data-status.jsx +81 -0
  19. package/client/components/setting-sync/setting-sync-form.jsx +6 -0
  20. package/client/components/setting-sync/setting-sync.jsx +8 -5
  21. package/client/components/sftp/list-table-ui.jsx +13 -15
  22. package/client/components/sftp/sftp-entry.jsx +4 -22
  23. package/client/components/shortcuts/shortcut-control.jsx +10 -1
  24. package/client/components/sidebar/bookmark-select.jsx +3 -1
  25. package/client/components/tabs/tab.jsx +7 -8
  26. package/client/components/tabs/tabs.styl +3 -0
  27. package/client/components/terminal/term-search.jsx +2 -1
  28. package/client/components/terminal/terminal.jsx +26 -9
  29. package/client/components/terminal-info/base.jsx +9 -4
  30. package/client/components/tree-list/bookmark-toolbar.jsx +2 -3
  31. package/client/components/tree-list/tree-list.jsx +36 -51
  32. package/client/components/tree-list/tree-search.jsx +1 -0
  33. package/client/store/bookmark-group.js +1 -2
  34. package/client/store/init-state.js +4 -0
  35. package/client/store/item.js +1 -2
  36. package/client/store/load-data.js +6 -1
  37. package/client/store/setting.js +1 -2
  38. package/client/store/store.js +13 -15
  39. package/client/store/sync.js +42 -7
  40. package/client/store/terminal-theme.js +4 -4
  41. package/client/store/ui-theme.js +3 -10
  42. package/client/store/watch.js +11 -0
  43. package/package.json +1 -1
  44. package/client/components/main/loading.jsx +0 -25
@@ -372,3 +372,4 @@ export const sshConfigLoadKey = 'ssh-config-loaded'
372
372
  export const sshConfigKey = 'ignore-ssh-config'
373
373
  export const connectionHoppingWarnKey = 'connectionHoppingWarnned'
374
374
  export const aiChatHistoryKey = 'ai-chat-history'
375
+ export const syncServerDataKey = 'sync-server-data'
@@ -0,0 +1,5 @@
1
+ import { osResolve } from './resolve'
2
+
3
+ export default function () {
4
+ return osResolve(window.store.appPath, 'electerm', 'session_logs')
5
+ }
@@ -61,6 +61,8 @@ export default {
61
61
  dataSyncSelected: 'all',
62
62
  baseURLAI: 'https://api.deepseek.com',
63
63
  modelAI: 'deepseek-chat',
64
- roleAI: '终端专家,提供不同系统下安全命令,解释用法及风险,用markdown格式',
65
- apiPathAI: '/chat/completions'
64
+ roleAI: '终端专家,提供不同系统下命令,简要解释用法,用markdown格式',
65
+ apiPathAI: '/chat/completions',
66
+ sessionLogPath: '',
67
+ sshSftpSplitView: false
66
68
  }
@@ -6,9 +6,5 @@ export default [
6
6
  {
7
7
  name: 'github',
8
8
  url: 'https://github.com/electerm/electerm/releases'
9
- },
10
- {
11
- name: 'sourceforge',
12
- url: 'https://sourceforge.net/projects/electerm.mirror/files/'
13
9
  }
14
10
  ]
@@ -5,10 +5,9 @@
5
5
  import {
6
6
  defaultBookmarkGroupId
7
7
  } from './constants'
8
- import { find } from 'lodash-es'
9
8
 
10
9
  export default (bookmarkGroups, id) => {
11
- const obj = find(bookmarkGroups, bg => {
10
+ const obj = bookmarkGroups.find(bg => {
12
11
  return bg.bookmarkIds.includes(id)
13
12
  })
14
13
  return obj ? obj.id : defaultBookmarkGroupId
@@ -19,10 +19,10 @@ import providers from './providers'
19
19
  const e = window.translate
20
20
  const defaultRoles = [
21
21
  {
22
- value: 'Terminal expert, provide safe commands for different OS, explain usage and risks, use markdown format'
22
+ value: 'Terminal expert, provide commands for different OS, explain usage briefly, use markdown format'
23
23
  },
24
24
  {
25
- value: '终端专家,提供不同系统下安全命令,解释用法及风险,用markdown格式'
25
+ value: '终端专家,提供不同系统下命令,简要解释用法,用markdown格式'
26
26
  }
27
27
  ]
28
28
 
@@ -0,0 +1,13 @@
1
+ import { lazy, Suspense } from 'react'
2
+
3
+ // Lazy load BatchOp
4
+ export const BatchOp = lazy(() => import('./batch-op'))
5
+
6
+ // Wrap BatchOp with Suspense
7
+ export default function BatchOpEntry (props) {
8
+ return (
9
+ <Suspense fallback={<div>Loading...</div>}>
10
+ <BatchOp {...props} />
11
+ </Suspense>
12
+ )
13
+ }
@@ -58,7 +58,7 @@ export default class BookmarkIndex extends PureComponent {
58
58
  this.setState({
59
59
  ready: true
60
60
  })
61
- }, 200)
61
+ }, 50)
62
62
  }
63
63
 
64
64
  componentWillUnmount () {
@@ -4,7 +4,7 @@ import {
4
4
  } from 'antd'
5
5
  import { InfoCircleOutlined } from '@ant-design/icons'
6
6
  import './footer.styl'
7
- import { paneMap, statusMap } from '../../common/constants'
7
+ import { statusMap } from '../../common/constants'
8
8
  import BatchInput from './batch-input'
9
9
  import encodes from '../bookmark-form/encodes'
10
10
  import { refs } from '../common/ref'
@@ -131,21 +131,7 @@ export default auto(function FooterEntry (props) {
131
131
  )
132
132
  }
133
133
 
134
- const { tabs, leftSidebarWidth, openedSideBar, currentTab } = props.store
135
- const pane = currentTab?.pane
136
- const type = currentTab?.type
137
- if (
138
- type === 'rdp' ||
139
- type === 'web' ||
140
- type === 'vnc' ||
141
- pane === paneMap.fileManager ||
142
- pane === paneMap.sftp ||
143
- !tabs.length
144
- ) {
145
- return (
146
- <div className='main-footer' />
147
- )
148
- }
134
+ const { leftSidebarWidth, openedSideBar, inActiveTerminal } = props.store
149
135
  const w = 43 + leftSidebarWidth
150
136
  const sideProps = openedSideBar
151
137
  ? {
@@ -157,6 +143,13 @@ export default auto(function FooterEntry (props) {
157
143
  : {
158
144
  className: 'main-footer'
159
145
  }
146
+ if (
147
+ !inActiveTerminal
148
+ ) {
149
+ return (
150
+ <div className='main-footer' {...sideProps} />
151
+ )
152
+ }
160
153
  return (
161
154
  <div {...sideProps}>
162
155
  <div className='terminal-footer-flex'>
@@ -0,0 +1,14 @@
1
+ import Icon from '@ant-design/icons'
2
+
3
+ // from https://icon-sets.iconify.design/codicon/case-sensitive/
4
+ const splitViewSvg = () => (
5
+ <svg width='1em' height='1em' viewBox='0 0 16 16'>
6
+ <rect x='1' y='1' width='14' height='14' stroke='currentColor' strokeWidth='1' fill='none' />
7
+ <line x1='8' y1='1' x2='8' y2='15' stroke='currentColor' strokeWidth='1' />
8
+ <polyline points='3,5 5,7 3,9' stroke='currentColor' strokeWidth='1' fill='none' />
9
+ <path d='M9 4H14V12H9V4Z' stroke='currentColor' strokeWidth='1' fill='none' />
10
+ <path d='M9 6H14' stroke='currentColor' strokeWidth='1' />
11
+ </svg>
12
+ )
13
+
14
+ export const SplitViewIcon = props => (<Icon component={splitViewSvg} {...props} />)
@@ -6,7 +6,7 @@ import UpdateCheck from './upgrade'
6
6
  import SettingModal from '../setting-panel/setting-modal'
7
7
  import TextEditor from '../text-editor/text-editor'
8
8
  import Sidebar from '../sidebar'
9
- import BatchOp from '../batch-op/batch-op'
9
+ import BatchOp from '../batch-op/batch-op-entry'
10
10
  import CssOverwrite from './css-overwrite'
11
11
  import UiTheme from './ui-theme'
12
12
  import CustomCss from './custom-css.jsx'
@@ -20,7 +20,6 @@ import ShortcutControl from '../shortcuts/shortcut-control.jsx'
20
20
  import { isMac, isWin } from '../../common/constants'
21
21
  import TermFullscreenControl from './term-fullscreen-control'
22
22
  import TerminalInfo from '../terminal-info/terminal-info'
23
- import { LoadingUI } from './loading'
24
23
  import { ConfigProvider, notification, message } from 'antd'
25
24
  import InfoModal from '../sidebar/info-modal.jsx'
26
25
  import RightSidePanel from '../side-panel-r/side-panel-r'
@@ -193,11 +192,14 @@ export default auto(function Index (props) {
193
192
  ...deepCopy(store.terminalInfoProps),
194
193
  ...pick(
195
194
  config,
196
- ['host', 'port', 'saveTerminalLogToFile', 'terminalInfos']
197
- ),
198
- ...pick(store, [
199
- 'appPath'
200
- ])
195
+ [
196
+ 'host',
197
+ 'port',
198
+ 'saveTerminalLogToFile',
199
+ 'terminalInfos',
200
+ 'sessionLogPath'
201
+ ]
202
+ )
201
203
  }
202
204
  const sshConfigProps = {
203
205
  ...pick(store, [
@@ -225,9 +227,6 @@ export default auto(function Index (props) {
225
227
  >
226
228
  <div {...ext1}>
227
229
  <ShortcutControl config={config} />
228
- <LoadingUI
229
- wsInited={wsInited}
230
- />
231
230
  <TermFullscreenControl
232
231
  terminalFullScreen={terminalFullScreen}
233
232
  />
@@ -16,7 +16,8 @@ import {
16
16
  } from '@ant-design/icons'
17
17
  import {
18
18
  Tooltip,
19
- message
19
+ message,
20
+ Splitter
20
21
  } from 'antd'
21
22
  import { pick } from 'lodash-es'
22
23
  import generate from '../../common/uid'
@@ -27,30 +28,36 @@ import {
27
28
  connectionMap,
28
29
  terminalRdpType,
29
30
  terminalVncType,
30
- terminalWebType
31
+ terminalWebType,
32
+ terminalTelnetType
31
33
  } from '../../common/constants'
34
+ import { SplitViewIcon } from '../icons/split-view'
32
35
  import { refs } from '../common/ref'
33
36
  import safeName from '../../common/safe-name'
34
37
  import './session.styl'
35
38
 
36
39
  const e = window.translate
40
+ const SplitterPane = Splitter.Panel
37
41
 
38
42
  export default class SessionWrapper extends Component {
39
43
  constructor (props) {
40
44
  super(props)
41
- // Add ref
42
45
  this.domRef = createRef()
43
46
  this.state = {
44
- enableSftp: false,
45
47
  cwd: '',
46
48
  sftpPathFollowSsh: !!props.config.sftpPathFollowSsh,
47
49
  key: Math.random(),
50
+ splitSize: [50, 50],
48
51
  sessionOptions: null,
49
52
  sessionId: generate(),
50
53
  delKeyPressed: false
51
54
  }
55
+ props.tab.sshSftpSplitView = !!props.config.sshSftpSplitView
52
56
  }
53
57
 
58
+ minWithForSplit = 640
59
+ minHeightForSplit = 400
60
+
54
61
  componentDidMount () {
55
62
  this.updateTab()
56
63
  // this.initEvent()
@@ -64,6 +71,43 @@ export default class SessionWrapper extends Component {
64
71
  return this.domRef.current
65
72
  }
66
73
 
74
+ handleSshSftpSplitView = () => {
75
+ const nv = !this.props.tab.sshSftpSplitView
76
+ this.editTab({
77
+ sshSftpSplitView: nv
78
+ })
79
+ }
80
+
81
+ canSplitView = () => {
82
+ const {
83
+ width,
84
+ height,
85
+ tab
86
+ } = this.props
87
+ if (tab.enableSsh === false) {
88
+ return false
89
+ }
90
+ return width > this.minWithForSplit ||
91
+ height > this.minHeightForSplit
92
+ }
93
+
94
+ getSplitDirection = () => {
95
+ const {
96
+ sshSftpSplitView
97
+ } = this.props.tab
98
+ if (!sshSftpSplitView || !this.canSplitView()) {
99
+ return 'tabed'
100
+ }
101
+ const {
102
+ width,
103
+ height
104
+ } = this.props
105
+ const ratio = width / height
106
+ const baseRatio = this.minWithForSplit / this.minHeightForSplit
107
+ const wider = ratio > baseRatio
108
+ return wider ? 'leftRight' : 'topDown'
109
+ }
110
+
67
111
  handleClick = () => {
68
112
  window.store.activeTabId = this.props.tab.id
69
113
  }
@@ -177,7 +221,7 @@ export default class SessionWrapper extends Component {
177
221
  const update = {
178
222
  pane
179
223
  }
180
- if (pane === paneMap.fileManager) {
224
+ if (pane === paneMap.fileManager || pane === paneMap.sftp) {
181
225
  this.setState({
182
226
  enableSftp: true
183
227
  })
@@ -204,10 +248,6 @@ export default class SessionWrapper extends Component {
204
248
  return this.props.width
205
249
  }
206
250
 
207
- getWidthSftp = () => {
208
- return this.props.width
209
- }
210
-
211
251
  renderTerminals = () => {
212
252
  const {
213
253
  sessionOptions,
@@ -218,7 +258,7 @@ export default class SessionWrapper extends Component {
218
258
  tab
219
259
  } = this.props
220
260
  const {
221
- pane, type
261
+ pane, type, sshSftpSplitView
222
262
  } = tab
223
263
  if (type === terminalWebType) {
224
264
  const webProps = {
@@ -269,13 +309,16 @@ export default class SessionWrapper extends Component {
269
309
  />
270
310
  )
271
311
  }
272
- const cls = pane === paneMap.terminal
312
+
313
+ const cls = pane === paneMap.terminal ||
314
+ pane === paneMap.ssh ||
315
+ (sshSftpSplitView && this.canSplitView())
273
316
  ? 'terms-box'
274
317
  : 'terms-box hide'
275
- const width = this.getWidth()
276
- const height = this.props.computeHeight(
277
- this.props.height
278
- )
318
+ const {
319
+ width,
320
+ height
321
+ } = this.calcTermWidthHeight()
279
322
  const themeConfig = copy(window.store.getThemeConfig())
280
323
  const logName = safeName(`${tab.title ? tab.title + '_' : ''}${tab.host ? tab.host + '_' : ''}${tab.id}`)
281
324
  const pops = {
@@ -312,6 +355,56 @@ export default class SessionWrapper extends Component {
312
355
  )
313
356
  }
314
357
 
358
+ isNotTerminalType = () => {
359
+ const { type } = this.props.tab
360
+ return type === terminalRdpType ||
361
+ type === terminalVncType ||
362
+ type === terminalWebType ||
363
+ type === terminalTelnetType
364
+ }
365
+
366
+ calcSftpWidthHeight = () => {
367
+ const {
368
+ width,
369
+ height
370
+ } = this.props
371
+ if (!this.canSplitView() || !this.props.tab.sshSftpSplitView) {
372
+ return {
373
+ width,
374
+ height
375
+ }
376
+ }
377
+ const direction = this.getSplitDirection()
378
+ const [, size2] = this.state.splitSize
379
+ const w = direction === 'leftRight' ? size2 * width / 100 : width
380
+ const h = direction === 'leftRight' ? height : size2 * height / 100
381
+ return {
382
+ width: w,
383
+ height: h
384
+ }
385
+ }
386
+
387
+ calcTermWidthHeight = () => {
388
+ const width = this.getWidth()
389
+ const height = this.props.computeHeight(
390
+ this.props.height
391
+ )
392
+ if (!this.canSplitView() || !this.props.tab.sshSftpSplitView) {
393
+ return {
394
+ width,
395
+ height
396
+ }
397
+ }
398
+ const direction = this.getSplitDirection()
399
+ const [size1] = this.state.splitSize
400
+ const w = direction === 'leftRight' ? size1 * width / 100 : width
401
+ const h = direction === 'leftRight' ? height : size1 * height / 100
402
+ return {
403
+ width: w,
404
+ height: h
405
+ }
406
+ }
407
+
315
408
  renderSftp = () => {
316
409
  const {
317
410
  sessionOptions,
@@ -320,23 +413,23 @@ export default class SessionWrapper extends Component {
320
413
  sftpPathFollowSsh,
321
414
  cwd
322
415
  } = this.state
323
- const { pane, type, id } = this.props.tab
416
+ const { pane, id, sshSftpSplitView } = this.props.tab
324
417
  if (
325
- type === terminalRdpType ||
326
- type === terminalVncType ||
327
- type === terminalWebType
418
+ this.isNotTerminalType()
328
419
  ) {
329
420
  return null
330
421
  }
331
422
  const height = this.props.computeHeight(
332
423
  this.props.height
333
424
  )
334
- const cls = pane === paneMap.terminal
335
- ? 'hide'
336
- : ''
425
+ const cls = pane === paneMap.fileManager || paneMap.sftp === pane ||
426
+ (sshSftpSplitView && this.canSplitView())
427
+ ? ''
428
+ : 'hide'
337
429
  const exts = {
338
430
  ...this.props,
339
431
  sftpPathFollowSsh,
432
+ sshSftpSplitView,
340
433
  cwd,
341
434
  pid: id,
342
435
  enableSftp,
@@ -344,7 +437,7 @@ export default class SessionWrapper extends Component {
344
437
  height,
345
438
  sessionId,
346
439
  pane,
347
- width: this.getWidthSftp()
440
+ ...this.calcSftpWidthHeight()
348
441
  }
349
442
  return (
350
443
  <div className={cls}>
@@ -416,18 +509,38 @@ export default class SessionWrapper extends Component {
416
509
  )
417
510
  }
418
511
 
419
- renderControl = () => {
420
- const { sftpPathFollowSsh } = this.state
421
- const { props } = this
422
- const { tab } = props
423
- const { pane, enableSsh, type } = tab
424
- if (
425
- type === terminalRdpType ||
426
- type === terminalVncType ||
427
- type === terminalWebType
428
- ) {
512
+ renderSplitToggle = () => {
513
+ if (!this.canSplitView() || this.isNotTerminalType()) {
514
+ return null
515
+ }
516
+ const title = e('sshSftpSplitView')
517
+ return (
518
+ <Tooltip title={title} placement='bottomLeft'>
519
+ <span
520
+ className='pointer mg1r split-view-toggle'
521
+ onClick={this.handleSshSftpSplitView}
522
+ >
523
+ <SplitViewIcon />
524
+ </span>
525
+ </Tooltip>
526
+ )
527
+ }
528
+
529
+ isSsh = () => {
530
+ const { tab } = this.props
531
+ return tab.authType
532
+ }
533
+
534
+ renderPaneControl = () => {
535
+ const {
536
+ sshSftpSplitView
537
+ } = this.props.tab
538
+ if (sshSftpSplitView && this.canSplitView()) {
429
539
  return null
430
540
  }
541
+ const { props } = this
542
+ const { tab } = props
543
+ const { pane } = tab
431
544
  const termType = tab?.type
432
545
  const isSsh = tab.authType
433
546
  const isLocal = !isSsh && (termType === connectionMap.local || !termType)
@@ -441,6 +554,51 @@ export default class SessionWrapper extends Component {
441
554
  if (isSsh || isLocal) {
442
555
  controls.push(isSsh ? paneMap.sftp : paneMap.fileManager)
443
556
  }
557
+ const simpleMapper = {
558
+ [paneMap.terminal]: 'T',
559
+ [paneMap.fileManager]: 'F',
560
+ [paneMap.ssh]: 'T'
561
+ }
562
+ return (
563
+ <div className='term-sftp-tabs fleft'>
564
+ {
565
+ controls.map((type, i) => {
566
+ const cls = classnames(
567
+ 'type-tab',
568
+ type,
569
+ {
570
+ active: types[i] === pane
571
+ }
572
+ )
573
+ return (
574
+ <span
575
+ className={cls}
576
+ key={type + '_' + i}
577
+ onClick={() => this.onChangePane(types[i])}
578
+ >
579
+ <span className='type-tab-txt'>
580
+ <span className='w500'>{e(type)}</span>
581
+ <span className='l500'>{simpleMapper[type]}</span>
582
+ <span className='type-tab-line' />
583
+ </span>
584
+ </span>
585
+ )
586
+ })
587
+ }
588
+ </div>
589
+ )
590
+ }
591
+
592
+ renderSftpPathFollowControl = () => {
593
+ const {
594
+ sftpPathFollowSsh
595
+ } = this.state
596
+ const { props } = this
597
+ const { tab } = props
598
+ const { pane, enableSsh, sshSftpSplitView } = tab
599
+ const termType = tab?.type
600
+ const isSsh = tab.authType
601
+ const isLocal = !isSsh && (termType === connectionMap.local || !termType)
444
602
  const checkTxt = e('sftpPathFollowSsh') + ' [Beta]'
445
603
  const checkProps = {
446
604
  onClick: this.toggleCheckSftpPathFollowSsh,
@@ -451,41 +609,11 @@ export default class SessionWrapper extends Component {
451
609
  }
452
610
  )
453
611
  }
454
- const simpleMapper = {
455
- [paneMap.terminal]: 'T',
456
- [paneMap.fileManager]: 'F',
457
- [paneMap.ssh]: 'T'
458
- }
612
+ const isS = pane === paneMap.terminal ||
613
+ pane === paneMap.ssh ||
614
+ sshSftpSplitView
459
615
  return (
460
- <div
461
- className='terminal-control fix'
462
- >
463
- <div className='term-sftp-tabs fleft'>
464
- {
465
- controls.map((type, i) => {
466
- const cls = classnames(
467
- 'type-tab',
468
- type,
469
- {
470
- active: types[i] === pane
471
- }
472
- )
473
- return (
474
- <span
475
- className={cls}
476
- key={type + '_' + i}
477
- onClick={() => this.onChangePane(types[i])}
478
- >
479
- <span className='type-tab-txt'>
480
- <span className='w500'>{e(type)}</span>
481
- <span className='l500'>{simpleMapper[type]}</span>
482
- <span className='type-tab-line' />
483
- </span>
484
- </span>
485
- )
486
- })
487
- }
488
- </div>
616
+ <>
489
617
  {
490
618
  (isSsh && enableSsh) || isLocal
491
619
  ? (
@@ -498,13 +626,101 @@ export default class SessionWrapper extends Component {
498
626
  : null
499
627
  }
500
628
  {
501
- this.renderDelTip(pane === paneMap.terminal)
629
+ this.renderDelTip(isS)
502
630
  }
631
+ </>
632
+ )
633
+ }
634
+
635
+ renderControl = () => {
636
+ if (
637
+ this.isNotTerminalType()
638
+ ) {
639
+ return null
640
+ }
641
+ return (
642
+ <div
643
+ className='terminal-control fix'
644
+ >
645
+ {this.renderPaneControl()}
646
+ {this.renderSftpPathFollowControl()}
647
+ {this.renderSplitToggle()}
503
648
  {this.renderTermControls()}
504
649
  </div>
505
650
  )
506
651
  }
507
652
 
653
+ onSplitResize = (sizes) => {
654
+ const direction = this.getSplitDirection()
655
+ const {
656
+ width,
657
+ height
658
+ } = this.props
659
+ const all = direction === 'leftRight' ? width : height
660
+ const size = sizes.map(d => d * 100 / all)
661
+ this.setState({
662
+ splitSize: size
663
+ })
664
+ }
665
+
666
+ renderViews = () => {
667
+ if (this.isNotTerminalType()) {
668
+ return this.renderTerminals()
669
+ }
670
+ const notSplitVew = !this.canSplitView() || !this.props.tab.sshSftpSplitView
671
+ const { pane } = this.props.tab
672
+ const show1 = notSplitVew && (pane === paneMap.terminal || pane === paneMap.ssh)
673
+ const show2 = notSplitVew && (pane === paneMap.fileManager || pane === paneMap.sftp)
674
+ const direction = this.getSplitDirection()
675
+ const layout = direction === 'leftRight' ? 'horizontal' : 'vertical'
676
+ const [size1, size2] = this.state.splitSize
677
+ const splitterProps = {
678
+ layout,
679
+ onResize: this.onSplitResize,
680
+ onResizeEnd: this.onSplitResize,
681
+ className: notSplitVew ? 'not-split-view' : '',
682
+ style: {
683
+ width: this.props.width + 'px',
684
+ height: this.props.height + 'px'
685
+ }
686
+ }
687
+ const paneProps = {
688
+ min: '20%',
689
+ max: '80%',
690
+ style: {
691
+ overflow: 'hidden'
692
+ }
693
+ }
694
+ const s1 = show1
695
+ ? '100%'
696
+ : show2
697
+ ? '0%'
698
+ : size1 + '%'
699
+ const s2 = show2
700
+ ? '100%'
701
+ : show1
702
+ ? '0%'
703
+ : size2 + '%'
704
+ const paneProps1 = {
705
+ ...paneProps,
706
+ size: s1
707
+ }
708
+ const paneProps2 = {
709
+ ...paneProps,
710
+ size: s2
711
+ }
712
+ return (
713
+ <Splitter {...splitterProps}>
714
+ <SplitterPane {...paneProps1}>
715
+ {this.renderTerminals()}
716
+ </SplitterPane>
717
+ <SplitterPane {...paneProps2}>
718
+ {this.renderSftp()}
719
+ </SplitterPane>
720
+ </Splitter>
721
+ )
722
+ }
723
+
508
724
  render () {
509
725
  const { pane } = this.props.tab
510
726
  const cls = classnames(
@@ -531,8 +747,7 @@ export default class SessionWrapper extends Component {
531
747
  {...divProps}
532
748
  >
533
749
  {this.renderControl()}
534
- {this.renderTerminals()}
535
- {this.renderSftp()}
750
+ {this.renderViews()}
536
751
  </div>
537
752
  )
538
753
  }
@@ -72,3 +72,5 @@
72
72
  display block !important
73
73
  width auto !important
74
74
  height auto !important
75
+ .not-split-view .ant-splitter-bar-dragger
76
+ display none