@electerm/electerm-react 1.60.56 → 1.70.1
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/client/common/constants.js +1 -0
- package/client/common/default-log-path.js +5 -0
- package/client/common/default-setting.js +3 -1
- package/client/common/find-bookmark-group-id.js +1 -2
- package/client/components/batch-op/batch-op-entry.jsx +13 -0
- package/client/components/bookmark-form/index.jsx +1 -1
- package/client/components/footer/footer-entry.jsx +9 -16
- package/client/components/icons/split-view.jsx +14 -0
- package/client/components/main/main.jsx +9 -10
- package/client/components/session/session.jsx +285 -70
- package/client/components/session/session.styl +2 -0
- package/client/components/setting-panel/on-tree-drop.js +10 -19
- package/client/components/setting-panel/setting-terminal.jsx +94 -20
- package/client/components/setting-panel/tab-settings.jsx +2 -1
- package/client/components/setting-sync/server-data-status.jsx +81 -0
- package/client/components/setting-sync/setting-sync-form.jsx +6 -0
- package/client/components/setting-sync/setting-sync.jsx +8 -5
- package/client/components/sftp/list-table-ui.jsx +13 -15
- package/client/components/sftp/sftp-entry.jsx +4 -22
- package/client/components/shortcuts/shortcut-control.jsx +10 -1
- package/client/components/tabs/tab.jsx +7 -8
- package/client/components/tabs/tabs.styl +3 -0
- package/client/components/terminal/term-search.jsx +2 -1
- package/client/components/terminal/terminal.jsx +26 -8
- package/client/components/terminal-info/base.jsx +9 -4
- package/client/components/tree-list/bookmark-toolbar.jsx +2 -3
- package/client/components/tree-list/tree-list.jsx +4 -7
- package/client/components/tree-list/tree-search.jsx +1 -0
- package/client/store/bookmark-group.js +1 -2
- package/client/store/init-state.js +3 -0
- package/client/store/item.js +1 -2
- package/client/store/load-data.js +1 -1
- package/client/store/setting.js +1 -2
- package/client/store/store.js +13 -15
- package/client/store/sync.js +42 -7
- package/client/store/terminal-theme.js +4 -4
- package/client/store/ui-theme.js +3 -10
- package/client/store/watch.js +6 -0
- package/package.json +1 -1
- 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'
|
|
@@ -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(
|
|
10
|
+
const obj = bookmarkGroups.find(bg => {
|
|
12
11
|
return bg.bookmarkIds.includes(id)
|
|
13
12
|
})
|
|
14
13
|
return obj ? obj.id : defaultBookmarkGroupId
|
|
@@ -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
|
+
}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from 'antd'
|
|
5
5
|
import { InfoCircleOutlined } from '@ant-design/icons'
|
|
6
6
|
import './footer.styl'
|
|
7
|
-
import {
|
|
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 {
|
|
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
|
-
[
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
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
|
|
276
|
-
|
|
277
|
-
|
|
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,
|
|
416
|
+
const { pane, id, sshSftpSplitView } = this.props.tab
|
|
324
417
|
if (
|
|
325
|
-
|
|
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.
|
|
335
|
-
|
|
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
|
-
|
|
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
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
const
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
[paneMap.ssh]: 'T'
|
|
458
|
-
}
|
|
612
|
+
const isS = pane === paneMap.terminal ||
|
|
613
|
+
pane === paneMap.ssh ||
|
|
614
|
+
sshSftpSplitView
|
|
459
615
|
return (
|
|
460
|
-
|
|
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(
|
|
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.
|
|
535
|
-
{this.renderSftp()}
|
|
750
|
+
{this.renderViews()}
|
|
536
751
|
</div>
|
|
537
752
|
)
|
|
538
753
|
}
|