@electerm/electerm-react 2.3.151 → 2.3.166
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 +4 -2
- package/client/common/db.js +2 -1
- package/client/common/init-setting-item.js +7 -0
- package/client/components/common/modal.jsx +89 -0
- package/client/components/common/modal.styl +77 -0
- package/client/components/common/notification-with-details.jsx +34 -0
- package/client/components/file-transfer/conflict-resolve.jsx +2 -1
- package/client/components/file-transfer/transfer-speed-format.js +6 -0
- package/client/components/file-transfer/transfer.jsx +5 -2
- package/client/components/file-transfer/transports-action-store.jsx +14 -1
- package/client/components/main/main.jsx +2 -0
- package/client/components/setting-panel/setting-common.jsx +4 -3
- package/client/components/setting-panel/start-session-select.jsx +146 -21
- package/client/components/setting-panel/text-bg-modal.jsx +15 -4
- package/client/components/sftp/file-info-modal.jsx +2 -1
- package/client/components/sftp/file-item.jsx +2 -0
- package/client/components/sidebar/info-modal.jsx +53 -34
- package/client/components/sidebar/info.styl +0 -7
- package/client/components/tabs/index.jsx +6 -58
- package/client/components/tabs/layout-menu.jsx +75 -0
- package/client/components/tabs/layout-select.jsx +60 -0
- package/client/components/tabs/tabs.styl +64 -0
- package/client/components/tabs/workspace-save-modal.jsx +117 -0
- package/client/components/tabs/workspace-select.jsx +79 -0
- package/client/components/terminal/attach-addon-custom.js +7 -1
- package/client/components/terminal/terminal-interactive.jsx +2 -1
- package/client/components/terminal/terminal.jsx +0 -1
- package/client/components/text-editor/text-editor.jsx +2 -1
- package/client/components/tree-list/move-item-modal.jsx +2 -1
- package/client/components/vnc/vnc-session.jsx +2 -2
- package/client/components/widgets/widget-control.jsx +4 -5
- package/client/components/widgets/widget-form.jsx +3 -8
- package/client/components/widgets/widget-instance.jsx +44 -9
- package/client/components/widgets/widget-notification-with-details.jsx +34 -0
- package/client/css/basic.styl +3 -1
- package/client/store/init-state.js +4 -0
- package/client/store/load-data.js +15 -6
- package/client/store/store.js +2 -0
- package/client/store/workspace.js +108 -0
- package/package.json +1 -1
|
@@ -102,7 +102,8 @@ export const settingMap = buildConst([
|
|
|
102
102
|
'quickCommands',
|
|
103
103
|
'addressBookmarks',
|
|
104
104
|
'profiles',
|
|
105
|
-
'widgets'
|
|
105
|
+
'widgets',
|
|
106
|
+
'workspaces'
|
|
106
107
|
])
|
|
107
108
|
|
|
108
109
|
export const staticNewItemTabs = new Set([
|
|
@@ -331,7 +332,8 @@ export const syncDataMaps = {
|
|
|
331
332
|
terminalThemes: ['terminalThemes'],
|
|
332
333
|
quickCommands: ['quickCommands'],
|
|
333
334
|
profiles: ['profiles'],
|
|
334
|
-
addressBookmarks: ['addressBookmarks']
|
|
335
|
+
addressBookmarks: ['addressBookmarks'],
|
|
336
|
+
workspaces: ['workspaces']
|
|
335
337
|
}
|
|
336
338
|
export const terminalTypes = [
|
|
337
339
|
'xterm-256color',
|
package/client/common/db.js
CHANGED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple modal component without animation
|
|
3
|
+
* Replaces antd Modal for better performance
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { CloseOutlined } from '@ant-design/icons'
|
|
7
|
+
import classnames from 'classnames'
|
|
8
|
+
import './modal.styl'
|
|
9
|
+
|
|
10
|
+
export default function Modal (props) {
|
|
11
|
+
const {
|
|
12
|
+
open,
|
|
13
|
+
title,
|
|
14
|
+
width = 520,
|
|
15
|
+
zIndex = 1000,
|
|
16
|
+
className,
|
|
17
|
+
wrapClassName,
|
|
18
|
+
children,
|
|
19
|
+
footer,
|
|
20
|
+
maskClosable = true,
|
|
21
|
+
onCancel
|
|
22
|
+
} = props
|
|
23
|
+
|
|
24
|
+
function handleMaskClick (e) {
|
|
25
|
+
if (e.target === e.currentTarget && maskClosable && onCancel) {
|
|
26
|
+
onCancel()
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function handleClose () {
|
|
31
|
+
if (onCancel) {
|
|
32
|
+
onCancel()
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!open) {
|
|
37
|
+
return null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const modalStyle = {
|
|
41
|
+
zIndex
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const contentStyle = {
|
|
45
|
+
width: typeof width === 'number' ? `${width}px` : width
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const cls = classnames(
|
|
49
|
+
'custom-modal-wrap',
|
|
50
|
+
wrapClassName,
|
|
51
|
+
className
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div className={cls} style={modalStyle}>
|
|
56
|
+
<div
|
|
57
|
+
className='custom-modal-mask'
|
|
58
|
+
onClick={handleMaskClick}
|
|
59
|
+
/>
|
|
60
|
+
<div className='custom-modal-container' onClick={handleMaskClick}>
|
|
61
|
+
<div
|
|
62
|
+
className='custom-modal-content'
|
|
63
|
+
style={contentStyle}
|
|
64
|
+
>
|
|
65
|
+
{title && (
|
|
66
|
+
<div className='custom-modal-header'>
|
|
67
|
+
<div className='custom-modal-title'>{title}</div>
|
|
68
|
+
<button
|
|
69
|
+
type='button'
|
|
70
|
+
className='custom-modal-close'
|
|
71
|
+
onClick={handleClose}
|
|
72
|
+
>
|
|
73
|
+
<CloseOutlined />
|
|
74
|
+
</button>
|
|
75
|
+
</div>
|
|
76
|
+
)}
|
|
77
|
+
<div className='custom-modal-body'>
|
|
78
|
+
{children}
|
|
79
|
+
</div>
|
|
80
|
+
{footer !== null && footer !== undefined && (
|
|
81
|
+
<div className='custom-modal-footer'>
|
|
82
|
+
{footer}
|
|
83
|
+
</div>
|
|
84
|
+
)}
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
.custom-modal-wrap
|
|
2
|
+
position fixed
|
|
3
|
+
top 0
|
|
4
|
+
left 0
|
|
5
|
+
right 0
|
|
6
|
+
bottom 0
|
|
7
|
+
overflow auto
|
|
8
|
+
outline 0
|
|
9
|
+
|
|
10
|
+
.custom-modal-mask
|
|
11
|
+
position fixed
|
|
12
|
+
top 0
|
|
13
|
+
left 0
|
|
14
|
+
right 0
|
|
15
|
+
bottom 0
|
|
16
|
+
background rgba(0, 0, 0, 0.45)
|
|
17
|
+
|
|
18
|
+
.custom-modal-container
|
|
19
|
+
position relative
|
|
20
|
+
display flex
|
|
21
|
+
align-items flex-start
|
|
22
|
+
justify-content center
|
|
23
|
+
min-height 100%
|
|
24
|
+
padding 24px
|
|
25
|
+
|
|
26
|
+
.custom-modal-content
|
|
27
|
+
position relative
|
|
28
|
+
background var(--main)
|
|
29
|
+
border-radius 8px
|
|
30
|
+
box-shadow 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 9px 28px 8px rgba(0, 0, 0, 0.05)
|
|
31
|
+
margin-top 50px
|
|
32
|
+
max-width calc(100vw - 48px)
|
|
33
|
+
|
|
34
|
+
.custom-modal-header
|
|
35
|
+
display flex
|
|
36
|
+
align-items center
|
|
37
|
+
justify-content space-between
|
|
38
|
+
padding 16px 24px
|
|
39
|
+
border-bottom 1px solid var(--main-darker)
|
|
40
|
+
|
|
41
|
+
.custom-modal-title
|
|
42
|
+
font-size 16px
|
|
43
|
+
font-weight 600
|
|
44
|
+
color var(--text)
|
|
45
|
+
line-height 1.5
|
|
46
|
+
flex 1
|
|
47
|
+
overflow hidden
|
|
48
|
+
text-overflow ellipsis
|
|
49
|
+
white-space nowrap
|
|
50
|
+
|
|
51
|
+
.custom-modal-close
|
|
52
|
+
display flex
|
|
53
|
+
align-items center
|
|
54
|
+
justify-content center
|
|
55
|
+
width 32px
|
|
56
|
+
height 32px
|
|
57
|
+
padding 0
|
|
58
|
+
margin-left 8px
|
|
59
|
+
background transparent
|
|
60
|
+
border none
|
|
61
|
+
border-radius 4px
|
|
62
|
+
color var(--text)
|
|
63
|
+
cursor pointer
|
|
64
|
+
transition background-color 0.2s
|
|
65
|
+
&:hover
|
|
66
|
+
background var(--main-darker)
|
|
67
|
+
&:focus
|
|
68
|
+
outline none
|
|
69
|
+
|
|
70
|
+
.custom-modal-body
|
|
71
|
+
padding 0 24px 24px 24px
|
|
72
|
+
color var(--text)
|
|
73
|
+
|
|
74
|
+
.custom-modal-footer
|
|
75
|
+
padding 12px 24px
|
|
76
|
+
border-top 1px solid var(--main-darker)
|
|
77
|
+
text-align right
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { notification } from 'antd'
|
|
2
|
+
import { CopyOutlined } from '@ant-design/icons'
|
|
3
|
+
import { copy } from '../../common/clipboard'
|
|
4
|
+
|
|
5
|
+
export function showMsg (message, type = 'success', serverInfo = null, duration = 10, description = '') {
|
|
6
|
+
const handleCopy = () => {
|
|
7
|
+
if (serverInfo && serverInfo.url) {
|
|
8
|
+
copy(serverInfo.url)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let desc = description
|
|
13
|
+
if (serverInfo) {
|
|
14
|
+
desc = (
|
|
15
|
+
<div>
|
|
16
|
+
{description && <div>{description}</div>}
|
|
17
|
+
<div style={{ display: 'flex', alignItems: 'center' }}>
|
|
18
|
+
<span>URL: <b>{serverInfo.url}</b></span>
|
|
19
|
+
<CopyOutlined
|
|
20
|
+
className='pointer mg1l'
|
|
21
|
+
onClick={handleCopy}
|
|
22
|
+
/>
|
|
23
|
+
</div>
|
|
24
|
+
<div>Path: <b>{serverInfo.path}</b></div>
|
|
25
|
+
</div>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
notification[type]({
|
|
30
|
+
message,
|
|
31
|
+
description: desc,
|
|
32
|
+
duration
|
|
33
|
+
})
|
|
34
|
+
}
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { Component } from 'react'
|
|
7
|
-
import {
|
|
7
|
+
import { Button } from 'antd'
|
|
8
|
+
import Modal from '../common/modal'
|
|
8
9
|
import { isString } from 'lodash-es'
|
|
9
10
|
import AnimateText from '../common/animate-text'
|
|
10
11
|
import formatTime from '../../common/time'
|
|
@@ -47,6 +47,12 @@ export const computePassedTime = (startTime) => {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export const computeLeftTime = (bytes, total, startTime) => {
|
|
50
|
+
if (total === 0) {
|
|
51
|
+
return {
|
|
52
|
+
leftTime: '0s',
|
|
53
|
+
leftTimeInt: 0
|
|
54
|
+
}
|
|
55
|
+
}
|
|
50
56
|
let now = Date.now()
|
|
51
57
|
if (now <= startTime) {
|
|
52
58
|
now = startTime + 1
|
|
@@ -71,6 +71,9 @@ export default class TransportAction extends Component {
|
|
|
71
71
|
this.transport = null
|
|
72
72
|
this.fromFile = null
|
|
73
73
|
refsTransfers.remove(this.id)
|
|
74
|
+
if (this.isFtp) {
|
|
75
|
+
window.initingFtpTabIds?.delete(this.tabId)
|
|
76
|
+
}
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
localCheckExist = (path) => {
|
|
@@ -185,9 +188,9 @@ export default class TransportAction extends Component {
|
|
|
185
188
|
const up = {}
|
|
186
189
|
const total = transfer.fromFile.size
|
|
187
190
|
let percent = total === 0
|
|
188
|
-
?
|
|
191
|
+
? 100
|
|
189
192
|
: Math.floor(100 * transferred / total)
|
|
190
|
-
percent = percent >= 100 ?
|
|
193
|
+
percent = percent >= 100 ? 100 : percent
|
|
191
194
|
up.percent = percent
|
|
192
195
|
up.status = 'active'
|
|
193
196
|
up.transferred = transferred
|
|
@@ -9,6 +9,8 @@ import { maxTransport } from '../../common/constants'
|
|
|
9
9
|
import { refsStatic } from '../common/ref'
|
|
10
10
|
// import { action } from 'manate'
|
|
11
11
|
|
|
12
|
+
window.initingFtpTabIds = new Set()
|
|
13
|
+
|
|
12
14
|
export default class TransportsActionStore extends Component {
|
|
13
15
|
componentDidMount () {
|
|
14
16
|
this.control()
|
|
@@ -71,7 +73,9 @@ export default class TransportsActionStore extends Component {
|
|
|
71
73
|
typeTo,
|
|
72
74
|
typeFrom,
|
|
73
75
|
inited,
|
|
74
|
-
id
|
|
76
|
+
id,
|
|
77
|
+
tabType,
|
|
78
|
+
tabId
|
|
75
79
|
} = tr
|
|
76
80
|
|
|
77
81
|
const isTransfer = typeTo !== typeFrom
|
|
@@ -80,6 +84,15 @@ export default class TransportsActionStore extends Component {
|
|
|
80
84
|
continue
|
|
81
85
|
}
|
|
82
86
|
|
|
87
|
+
// For ftp transfers, ensure only one per tabId is inited
|
|
88
|
+
if (tabType === 'ftp') {
|
|
89
|
+
const hasInited = fileTransfers.some(t => t.tabId === tabId && t.inited && t.id !== id)
|
|
90
|
+
if (hasInited || window.initingFtpTabIds.has(tabId)) {
|
|
91
|
+
continue
|
|
92
|
+
}
|
|
93
|
+
window.initingFtpTabIds.add(tabId)
|
|
94
|
+
}
|
|
95
|
+
|
|
83
96
|
if (count < maxTransport) {
|
|
84
97
|
count++
|
|
85
98
|
refsStatic.get('transfer-queue')?.addToQueue(
|
|
@@ -31,6 +31,7 @@ import AIChat from '../ai/ai-chat'
|
|
|
31
31
|
import Opacity from '../common/opacity'
|
|
32
32
|
import MoveItemModal from '../tree-list/move-item-modal'
|
|
33
33
|
import InputContextMenu from '../common/input-context-menu'
|
|
34
|
+
import WorkspaceSaveModal from '../tabs/workspace-save-modal'
|
|
34
35
|
import { pick } from 'lodash-es'
|
|
35
36
|
import deepCopy from 'json-deep-copy'
|
|
36
37
|
import './wrapper.styl'
|
|
@@ -305,6 +306,7 @@ export default auto(function Index (props) {
|
|
|
305
306
|
<ConnectionHoppingWarning {...warningProps} />
|
|
306
307
|
<TerminalCmdSuggestions {...cmdSuggestionsProps} />
|
|
307
308
|
<TransferQueue />
|
|
309
|
+
<WorkspaceSaveModal store={store} />
|
|
308
310
|
</div>
|
|
309
311
|
</ConfigProvider>
|
|
310
312
|
)
|
|
@@ -518,6 +518,7 @@ export default class SettingCommon extends Component {
|
|
|
518
518
|
onStartSessions: props.config.onStartSessions,
|
|
519
519
|
bookmarks: props.bookmarks,
|
|
520
520
|
bookmarkGroups: props.bookmarkGroups,
|
|
521
|
+
workspaces: props.store.workspaces,
|
|
521
522
|
onChangeStartSessions: this.onChangeStartSessions
|
|
522
523
|
}
|
|
523
524
|
return (
|
|
@@ -528,7 +529,7 @@ export default class SettingCommon extends Component {
|
|
|
528
529
|
<Select
|
|
529
530
|
value={modifier}
|
|
530
531
|
onChange={this.handleChangeModifier}
|
|
531
|
-
className='
|
|
532
|
+
className='width100'
|
|
532
533
|
popupMatchSelectWidth={false}
|
|
533
534
|
showSearch
|
|
534
535
|
>
|
|
@@ -536,10 +537,10 @@ export default class SettingCommon extends Component {
|
|
|
536
537
|
modifiers.map(this.renderOption)
|
|
537
538
|
}
|
|
538
539
|
</Select>
|
|
539
|
-
<span className='
|
|
540
|
+
<span className='mg1x'>+</span>
|
|
540
541
|
<Select
|
|
541
542
|
value={key}
|
|
542
|
-
className='
|
|
543
|
+
className='width100'
|
|
543
544
|
onChange={this.handleChangeKey}
|
|
544
545
|
popupMatchSelectWidth={false}
|
|
545
546
|
showSearch
|
|
@@ -1,15 +1,27 @@
|
|
|
1
|
-
import { TreeSelect } from 'antd'
|
|
2
|
-
import {
|
|
1
|
+
import { TreeSelect, Tabs, Select, Empty } from 'antd'
|
|
2
|
+
import { useState } from 'react'
|
|
3
3
|
import copy from 'json-deep-copy'
|
|
4
4
|
import { createTitleWithTag } from '../../common/create-title'
|
|
5
|
+
import {
|
|
6
|
+
AppstoreOutlined,
|
|
7
|
+
BookOutlined
|
|
8
|
+
} from '@ant-design/icons'
|
|
9
|
+
import HelpIcon from '../common/help-icon'
|
|
5
10
|
|
|
6
11
|
const e = window.translate
|
|
7
12
|
const { SHOW_CHILD } = TreeSelect
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
function BookmarkSelect (props) {
|
|
15
|
+
const {
|
|
16
|
+
bookmarks,
|
|
17
|
+
bookmarkGroups,
|
|
18
|
+
onStartSessions,
|
|
19
|
+
onChangeStartSessions
|
|
20
|
+
} = props
|
|
21
|
+
|
|
22
|
+
const buildData = () => {
|
|
23
|
+
const cats = bookmarkGroups
|
|
24
|
+
const tree = bookmarks
|
|
13
25
|
.reduce((p, k) => {
|
|
14
26
|
return {
|
|
15
27
|
...p,
|
|
@@ -64,28 +76,141 @@ export default class StartSessionSelect extends PureComponent {
|
|
|
64
76
|
...(d.bookmarkIds || []).map(buildLeaf)
|
|
65
77
|
].filter(d => d)
|
|
66
78
|
}
|
|
67
|
-
// if (!r.children.length) {
|
|
68
|
-
// return ''
|
|
69
|
-
// }
|
|
70
79
|
return r
|
|
71
80
|
}).filter(d => d)
|
|
72
81
|
return level1
|
|
73
82
|
}
|
|
74
83
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
// onStartSessions is array for bookmarks
|
|
85
|
+
const value = Array.isArray(onStartSessions) ? onStartSessions : []
|
|
86
|
+
|
|
87
|
+
const rProps = {
|
|
88
|
+
treeData: buildData(),
|
|
89
|
+
value: copy(value),
|
|
90
|
+
onChange: onChangeStartSessions,
|
|
91
|
+
treeCheckable: true,
|
|
92
|
+
showCheckedStrategy: SHOW_CHILD,
|
|
93
|
+
placeholder: e('pleaseSelect'),
|
|
94
|
+
style: {
|
|
95
|
+
width: '100%'
|
|
86
96
|
}
|
|
97
|
+
}
|
|
98
|
+
return (
|
|
99
|
+
<TreeSelect {...rProps} />
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function WorkspaceSelect (props) {
|
|
104
|
+
const {
|
|
105
|
+
workspaces,
|
|
106
|
+
onStartSessions,
|
|
107
|
+
onChangeStartSessions
|
|
108
|
+
} = props
|
|
109
|
+
|
|
110
|
+
if (!workspaces.length) {
|
|
87
111
|
return (
|
|
88
|
-
<
|
|
112
|
+
<Empty
|
|
113
|
+
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
|
114
|
+
description={e('noWorkspaces')}
|
|
115
|
+
/>
|
|
89
116
|
)
|
|
90
117
|
}
|
|
118
|
+
|
|
119
|
+
// onStartSessions is string for workspace
|
|
120
|
+
const value = typeof onStartSessions === 'string' ? onStartSessions : undefined
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<Select
|
|
124
|
+
value={value}
|
|
125
|
+
onChange={onChangeStartSessions}
|
|
126
|
+
placeholder={e('workspaces')}
|
|
127
|
+
style={{ width: '100%' }}
|
|
128
|
+
allowClear
|
|
129
|
+
>
|
|
130
|
+
{workspaces.map(w => (
|
|
131
|
+
<Select.Option key={w.id} value={w.id}>
|
|
132
|
+
{w.name}
|
|
133
|
+
</Select.Option>
|
|
134
|
+
))}
|
|
135
|
+
</Select>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export default function StartSessionSelect (props) {
|
|
140
|
+
const {
|
|
141
|
+
onStartSessions,
|
|
142
|
+
bookmarks,
|
|
143
|
+
bookmarkGroups,
|
|
144
|
+
workspaces,
|
|
145
|
+
onChangeStartSessions
|
|
146
|
+
} = props
|
|
147
|
+
|
|
148
|
+
// Determine initial tab based on what's configured
|
|
149
|
+
// string = workspace, array = bookmarks
|
|
150
|
+
const getInitialTab = () => {
|
|
151
|
+
if (typeof onStartSessions === 'string' && onStartSessions) {
|
|
152
|
+
return 'workspaces'
|
|
153
|
+
}
|
|
154
|
+
return 'bookmarks'
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const [activeTab, setActiveTab] = useState(getInitialTab)
|
|
158
|
+
|
|
159
|
+
// When switching tabs, clear the value if needed
|
|
160
|
+
const handleTabChange = (key) => {
|
|
161
|
+
setActiveTab(key)
|
|
162
|
+
// Reset to appropriate default when switching
|
|
163
|
+
if (key === 'bookmarks' && typeof onStartSessions === 'string') {
|
|
164
|
+
onChangeStartSessions([])
|
|
165
|
+
} else if (key === 'workspaces' && Array.isArray(onStartSessions)) {
|
|
166
|
+
onChangeStartSessions(undefined)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const tabItems = [
|
|
171
|
+
{
|
|
172
|
+
key: 'bookmarks',
|
|
173
|
+
label: (
|
|
174
|
+
<span>
|
|
175
|
+
<BookOutlined /> {e('bookmarks')}
|
|
176
|
+
</span>
|
|
177
|
+
)
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
key: 'workspaces',
|
|
181
|
+
label: (
|
|
182
|
+
<span>
|
|
183
|
+
<AppstoreOutlined /> {e('workspaces')}
|
|
184
|
+
<HelpIcon link='https://github.com/electerm/electerm/wiki/Workspace-Feature' />
|
|
185
|
+
</span>
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<div>
|
|
192
|
+
<Tabs
|
|
193
|
+
items={tabItems}
|
|
194
|
+
size='small'
|
|
195
|
+
activeKey={activeTab}
|
|
196
|
+
onChange={handleTabChange}
|
|
197
|
+
/>
|
|
198
|
+
{activeTab === 'bookmarks'
|
|
199
|
+
? (
|
|
200
|
+
<BookmarkSelect
|
|
201
|
+
bookmarks={bookmarks}
|
|
202
|
+
bookmarkGroups={bookmarkGroups}
|
|
203
|
+
onStartSessions={onStartSessions}
|
|
204
|
+
onChangeStartSessions={onChangeStartSessions}
|
|
205
|
+
/>
|
|
206
|
+
)
|
|
207
|
+
: (
|
|
208
|
+
<WorkspaceSelect
|
|
209
|
+
workspaces={workspaces}
|
|
210
|
+
onStartSessions={onStartSessions}
|
|
211
|
+
onChangeStartSessions={onChangeStartSessions}
|
|
212
|
+
/>
|
|
213
|
+
)}
|
|
214
|
+
</div>
|
|
215
|
+
)
|
|
91
216
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import {
|
|
3
|
-
Modal,
|
|
4
3
|
Input,
|
|
5
4
|
InputNumber,
|
|
6
5
|
Space,
|
|
7
6
|
Typography,
|
|
8
|
-
Select
|
|
7
|
+
Select,
|
|
8
|
+
Button
|
|
9
9
|
} from 'antd'
|
|
10
|
+
import Modal from '../common/modal'
|
|
10
11
|
import { ColorPicker } from '../bookmark-form/common/color-picker.jsx'
|
|
11
12
|
|
|
12
13
|
const { TextArea } = Input
|
|
@@ -47,14 +48,24 @@ export default function TextBgModal ({
|
|
|
47
48
|
setFontFamily(initialFontFamily)
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
const footer = (
|
|
52
|
+
<>
|
|
53
|
+
<Button onClick={handleCancel}>
|
|
54
|
+
{e('cancel')}
|
|
55
|
+
</Button>
|
|
56
|
+
<Button type='primary' onClick={handleOk} className='mg1l'>
|
|
57
|
+
{e('ok')}
|
|
58
|
+
</Button>
|
|
59
|
+
</>
|
|
60
|
+
)
|
|
61
|
+
|
|
50
62
|
return (
|
|
51
63
|
<Modal
|
|
52
64
|
title={e('terminalBackgroundText')}
|
|
53
65
|
open={visible}
|
|
54
|
-
onOk={handleOk}
|
|
55
66
|
onCancel={handleCancel}
|
|
56
67
|
width={500}
|
|
57
|
-
|
|
68
|
+
footer={footer}
|
|
58
69
|
>
|
|
59
70
|
<div className='pd1'>
|
|
60
71
|
<Space direction='vertical' size='large' style={{ width: '100%' }}>
|
|
@@ -183,6 +183,7 @@ export default class FileSection extends React.Component {
|
|
|
183
183
|
toPath,
|
|
184
184
|
id: generate(),
|
|
185
185
|
host: this.props.tab?.host,
|
|
186
|
+
tabType: this.props.tab?.type,
|
|
186
187
|
...createTransferProps(this.props),
|
|
187
188
|
operation
|
|
188
189
|
})
|
|
@@ -736,6 +737,7 @@ export default class FileSection extends React.Component {
|
|
|
736
737
|
toPath = resolve(toPath, name)
|
|
737
738
|
const obj = {
|
|
738
739
|
host: this.props.tab?.host,
|
|
740
|
+
tabType: this.props.tab?.type,
|
|
739
741
|
typeFrom: type,
|
|
740
742
|
typeTo,
|
|
741
743
|
fromPath: resolve(path, name),
|