@electerm/electerm-react 1.51.3 → 1.51.18
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 +15 -1
- package/client/common/db.js +10 -9
- package/client/common/default-setting.js +0 -1
- package/client/common/is-color-dark.js +15 -11
- package/client/common/new-terminal.js +2 -5
- package/client/common/reverse-color.js +12 -0
- package/client/common/ws.js +4 -1
- package/client/components/batch-op/batch-op.jsx +16 -5
- package/client/components/bookmark-form/index.jsx +1 -1
- package/client/components/bookmark-form/render-connection-hopping.jsx +25 -2
- package/client/components/bookmark-form/ssh-form.jsx +4 -25
- package/client/components/bookmark-form/tree-delete.jsx +5 -10
- package/client/components/bookmark-form/use-submit.jsx +6 -15
- package/client/components/bookmark-form/use-ui.jsx +1 -2
- package/client/components/common/connection-hopping-warning-text.jsx +36 -0
- package/client/components/common/drag-handle.jsx +60 -0
- package/client/components/common/drag-handle.styl +12 -0
- package/client/components/context-menu/context-menu.styl +5 -5
- package/client/components/context-menu/history.jsx +2 -11
- package/client/components/context-menu/sub-tab-menu.jsx +1 -1
- package/client/components/footer/footer-entry.jsx +1 -6
- package/client/components/layout/layout-item.jsx +2 -2
- package/client/components/layout/layout.jsx +3 -2
- package/client/components/main/connection-hopping-warnning.jsx +45 -0
- package/client/components/main/error-wrapper.jsx +120 -5
- package/client/components/main/main.jsx +32 -8
- package/client/components/main/upgrade.jsx +4 -9
- package/client/components/main/wrapper.styl +2 -1
- package/client/components/profile/profile-form-ssh.jsx +1 -1
- package/client/components/rdp/resolution-edit.jsx +3 -5
- package/client/components/session/session.jsx +22 -3
- package/client/components/session/session.styl +3 -2
- package/client/components/setting-panel/list.styl +0 -1
- package/client/components/setting-panel/on-tree-drop.js +5 -5
- package/client/components/setting-panel/setting-common.jsx +28 -7
- package/client/components/setting-panel/setting-modal.jsx +0 -12
- package/client/components/setting-panel/setting-terminal.jsx +7 -4
- package/client/components/sftp/confirm-modal-store.jsx +3 -11
- package/client/components/sftp/file-mode-modal.jsx +2 -2
- package/client/components/sftp/sftp-entry.jsx +4 -7
- package/client/components/sftp/transfer-conflict-store.jsx +70 -69
- package/client/components/sftp/transport-action-store.jsx +42 -49
- package/client/components/sftp/transports-action-store.jsx +15 -15
- package/client/components/sftp/transports-ui-store.jsx +9 -5
- package/client/components/side-panel-r/side-panel-r.jsx +13 -40
- package/client/components/sidebar/bookmark-select.jsx +1 -4
- package/client/components/sidebar/bookmark.jsx +4 -63
- package/client/components/sidebar/history-item.jsx +34 -0
- package/client/components/sidebar/history.jsx +17 -52
- package/client/components/sidebar/index.jsx +5 -40
- package/client/components/sidebar/side-panel.jsx +27 -51
- package/client/components/sidebar/sidebar-panel.jsx +107 -0
- package/client/components/sidebar/sidebar.styl +14 -9
- package/client/components/sidebar/transfer-list-control.jsx +1 -0
- package/client/components/sidebar/transfer.styl +1 -1
- package/client/components/sidebar/transport-ui.jsx +179 -37
- package/client/components/ssh-config/load-ssh-configs.jsx +106 -0
- package/client/components/ssh-config/ssh-config-item.jsx +26 -0
- package/client/components/ssh-config/ssh-config-load-notify.jsx +60 -0
- package/client/components/tabs/index.jsx +4 -4
- package/client/components/tabs/tab.jsx +28 -20
- package/client/components/tabs/tabs.styl +6 -1
- package/client/components/terminal/index.jsx +4 -18
- package/client/components/tree-list/bookmark-toolbar.jsx +203 -0
- package/client/components/tree-list/bookmark-transport.jsx +2 -0
- package/client/components/tree-list/tree-list.jsx +33 -42
- package/client/entry/worker.js +5 -3
- package/client/store/bookmark-group.js +5 -18
- package/client/store/bookmark.js +43 -1
- package/client/store/common.js +13 -9
- package/client/store/db-upgrade.js +0 -2
- package/client/store/index.js +45 -53
- package/client/store/init-state.js +20 -23
- package/client/store/item.js +0 -19
- package/client/store/load-data.js +7 -10
- package/client/store/setting.js +10 -66
- package/client/store/sidebar.js +7 -8
- package/client/store/sync.js +7 -8
- package/client/store/tab.js +93 -14
- package/client/store/terminal-theme.js +1 -1
- package/client/store/transfer-history.js +3 -9
- package/client/store/transfer-list.js +67 -75
- package/client/store/ui-theme.js +0 -9
- package/client/store/watch.js +17 -9
- package/package.json +1 -1
- package/client/components/setting-panel/tab-history.jsx +0 -43
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* transporter UI component
|
|
3
3
|
*/
|
|
4
|
-
import
|
|
4
|
+
import { useRef } from 'react'
|
|
5
5
|
import Tag from '../sftp/transfer-tag'
|
|
6
|
+
import { Flex } from 'antd'
|
|
6
7
|
import {
|
|
7
8
|
CloseCircleOutlined,
|
|
8
9
|
PlayCircleOutlined,
|
|
9
|
-
PauseCircleOutlined
|
|
10
|
+
PauseCircleOutlined,
|
|
11
|
+
VerticalAlignTopOutlined
|
|
10
12
|
} from '@ant-design/icons'
|
|
13
|
+
import { action } from 'manate'
|
|
14
|
+
import { addClass, removeClass } from '../../common/class'
|
|
11
15
|
import './transfer.styl'
|
|
12
16
|
|
|
13
17
|
const e = window.translate
|
|
14
18
|
|
|
15
19
|
export default function Transporter (props) {
|
|
20
|
+
const dom = useRef()
|
|
16
21
|
const {
|
|
17
22
|
fromPath,
|
|
18
23
|
toPath,
|
|
@@ -29,12 +34,109 @@ export default function Transporter (props) {
|
|
|
29
34
|
inited,
|
|
30
35
|
id
|
|
31
36
|
} = props.transfer
|
|
37
|
+
const { index } = props
|
|
38
|
+
const onDragCls = 'ondrag-tr'
|
|
39
|
+
const onDragOverCls = 'dragover-tr'
|
|
40
|
+
function moveToTop () {
|
|
41
|
+
action(function () {
|
|
42
|
+
const arr = window.store.fileTransfers
|
|
43
|
+
if (index > 0) {
|
|
44
|
+
const [item] = arr.splice(index, 1)
|
|
45
|
+
arr.unshift(item)
|
|
46
|
+
}
|
|
47
|
+
})()
|
|
48
|
+
}
|
|
32
49
|
function cancel () {
|
|
33
50
|
window.store.cancelTransfer(id)
|
|
34
51
|
}
|
|
35
52
|
function handlePauseOrResume () {
|
|
36
53
|
window.store.toggleTransfer(id)
|
|
37
54
|
}
|
|
55
|
+
|
|
56
|
+
function clearCls () {
|
|
57
|
+
document.querySelectorAll('.' + onDragOverCls).forEach((d) => {
|
|
58
|
+
removeClass(d, onDragOverCls)
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function onDrag () {
|
|
63
|
+
addClass(dom.current, onDragCls)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function onDragEnter () {
|
|
67
|
+
clearCls()
|
|
68
|
+
addClass(dom.current, onDragOverCls)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function onDragExit () {
|
|
72
|
+
// debug('ondragexit')
|
|
73
|
+
// let {target} = e
|
|
74
|
+
// removeClass(target, 'sftp-dragover')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function onDragLeave (e) {
|
|
78
|
+
// debug('ondragleave')
|
|
79
|
+
const { target } = e
|
|
80
|
+
removeClass(target, onDragOverCls)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function onDragOver (e) {
|
|
84
|
+
// debug('ondragover')
|
|
85
|
+
// debug(e.target)
|
|
86
|
+
// removeClass(dom.current, 'sftp-dragover')
|
|
87
|
+
e.preventDefault()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function onDragStart (e) {
|
|
91
|
+
// debug('ondragstart')
|
|
92
|
+
// debug(e.target)
|
|
93
|
+
e.dataTransfer.setData('id', JSON.stringify(dom.current.getAttribute('data-id')))
|
|
94
|
+
// e.effectAllowed = 'copyMove'
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function onDrop (e) {
|
|
98
|
+
e.preventDefault()
|
|
99
|
+
const { target } = e
|
|
100
|
+
if (!target) {
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
let onDropTab = target
|
|
104
|
+
while (onDropTab) {
|
|
105
|
+
if (onDropTab.classList && onDropTab.classList.contains('sftp-transport')) {
|
|
106
|
+
break
|
|
107
|
+
}
|
|
108
|
+
onDropTab = onDropTab.parentElement
|
|
109
|
+
}
|
|
110
|
+
const fromId = JSON.parse(e.dataTransfer.getData('id'))
|
|
111
|
+
if (!onDropTab || !fromId) {
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const dropId = onDropTab.getAttribute('data-id')
|
|
116
|
+
if (!dropId || dropId === fromId) {
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const arr = window.store.fileTransfers
|
|
121
|
+
const indexFrom = arr.findIndex(t => t.id === fromId)
|
|
122
|
+
let indexDrop = arr.findIndex(t => t.id === dropId)
|
|
123
|
+
if (indexFrom >= 0 && indexDrop >= 0) {
|
|
124
|
+
// Reorder tabs and update batch
|
|
125
|
+
action(function () {
|
|
126
|
+
const [tr] = arr.splice(indexFrom, 1)
|
|
127
|
+
if (indexFrom < indexDrop) {
|
|
128
|
+
indexDrop = indexDrop - 1
|
|
129
|
+
}
|
|
130
|
+
arr.splice(indexDrop, 0, tr)
|
|
131
|
+
})()
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function onDragEnd (e) {
|
|
136
|
+
removeClass(dom.current, onDragCls)
|
|
137
|
+
clearCls()
|
|
138
|
+
e && e.dataTransfer && e.dataTransfer.clearData()
|
|
139
|
+
}
|
|
38
140
|
const isTransfer = typeTo !== typeFrom
|
|
39
141
|
const Icon = !pausing ? PauseCircleOutlined : PlayCircleOutlined
|
|
40
142
|
const pauseTitle = pausing ? e('resume') : e('pause')
|
|
@@ -49,6 +151,14 @@ export default function Transporter (props) {
|
|
|
49
151
|
title={e('cancel')}
|
|
50
152
|
/>
|
|
51
153
|
)
|
|
154
|
+
const toTopIcon = index === 0
|
|
155
|
+
? null
|
|
156
|
+
: (
|
|
157
|
+
<VerticalAlignTopOutlined
|
|
158
|
+
className='transfer-control-icon pointer hover-black font14'
|
|
159
|
+
onClick={moveToTop}
|
|
160
|
+
/>
|
|
161
|
+
)
|
|
52
162
|
const controlIcon = isTransfer
|
|
53
163
|
? (
|
|
54
164
|
<Icon
|
|
@@ -58,41 +168,73 @@ export default function Transporter (props) {
|
|
|
58
168
|
/>
|
|
59
169
|
)
|
|
60
170
|
: null
|
|
171
|
+
const flexProps = {
|
|
172
|
+
className: cls,
|
|
173
|
+
gap: 3,
|
|
174
|
+
title,
|
|
175
|
+
ref: dom,
|
|
176
|
+
id: `transfer-unit-${id}`,
|
|
177
|
+
draggable: true,
|
|
178
|
+
'data-id': id,
|
|
179
|
+
onDrag,
|
|
180
|
+
onDragEnter,
|
|
181
|
+
onDragExit,
|
|
182
|
+
onDragLeave,
|
|
183
|
+
onDragOver,
|
|
184
|
+
onDragStart,
|
|
185
|
+
onDrop,
|
|
186
|
+
onDragEnd
|
|
187
|
+
}
|
|
61
188
|
return (
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
>
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
>
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
189
|
+
<Flex
|
|
190
|
+
{...flexProps}
|
|
191
|
+
>
|
|
192
|
+
<Flex>
|
|
193
|
+
<Tag
|
|
194
|
+
transfer={{
|
|
195
|
+
typeTo,
|
|
196
|
+
typeFrom,
|
|
197
|
+
error,
|
|
198
|
+
inited
|
|
199
|
+
}}
|
|
200
|
+
/>
|
|
201
|
+
</Flex>
|
|
202
|
+
<Flex>
|
|
203
|
+
<span
|
|
204
|
+
className='sftp-file sftp-local-file elli'
|
|
205
|
+
title={fromPath}
|
|
206
|
+
>{fromPathReal || fromPath}
|
|
207
|
+
</span>
|
|
208
|
+
</Flex>
|
|
209
|
+
<Flex>
|
|
210
|
+
<span className='sftp-transfer-arrow'>
|
|
211
|
+
→
|
|
212
|
+
</span>
|
|
213
|
+
</Flex>
|
|
214
|
+
<Flex>
|
|
215
|
+
<span
|
|
216
|
+
className='sftp-file sftp-remote-file elli'
|
|
217
|
+
>{toPathReal || toPath}
|
|
218
|
+
</span>
|
|
219
|
+
</Flex>
|
|
220
|
+
<Flex>
|
|
221
|
+
<span
|
|
222
|
+
className='sftp-file-percent'
|
|
223
|
+
>
|
|
224
|
+
{percent || 0}%
|
|
225
|
+
{speed ? `(${speed})` : null}
|
|
226
|
+
</span>
|
|
227
|
+
</Flex>
|
|
228
|
+
<Flex>
|
|
229
|
+
<span
|
|
230
|
+
className='sftp-file-percent'
|
|
231
|
+
>
|
|
232
|
+
{passedTime || '-'}|{leftTime || '-'}
|
|
233
|
+
</span>
|
|
234
|
+
</Flex>
|
|
235
|
+
<Flex>{controlIcon}</Flex>
|
|
236
|
+
<Flex>{cancelIcon}</Flex>
|
|
237
|
+
<Flex>{toTopIcon}</Flex>
|
|
238
|
+
</Flex>
|
|
97
239
|
)
|
|
98
240
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Modal,
|
|
3
|
+
Spin,
|
|
4
|
+
Button,
|
|
5
|
+
Empty
|
|
6
|
+
} from 'antd'
|
|
7
|
+
import { useState, useEffect } from 'react'
|
|
8
|
+
import SshConfigItem from './ssh-config-item'
|
|
9
|
+
import * as ls from '../../common/safe-local-storage'
|
|
10
|
+
import {
|
|
11
|
+
sshConfigLoadKey
|
|
12
|
+
} from '../../common/constants'
|
|
13
|
+
import { ReloadOutlined } from '@ant-design/icons'
|
|
14
|
+
|
|
15
|
+
const e = window.translate
|
|
16
|
+
|
|
17
|
+
export default function LoadSshConfigs (props) {
|
|
18
|
+
const [loading, setLoading] = useState(false)
|
|
19
|
+
const { sshConfigs } = props
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
store
|
|
23
|
+
} = window
|
|
24
|
+
const {
|
|
25
|
+
showSshConfigModal
|
|
26
|
+
} = props
|
|
27
|
+
const handleCancel = function () {
|
|
28
|
+
store.showSshConfigModal = false
|
|
29
|
+
}
|
|
30
|
+
const loadSshConfig = async function () {
|
|
31
|
+
setLoading(true)
|
|
32
|
+
await store.fetchSshConfigItems()
|
|
33
|
+
setLoading(false)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const handleLoadSshConfig = function () {
|
|
37
|
+
store.showSshConfigModal = false
|
|
38
|
+
store.addSshConfigs(sshConfigs)
|
|
39
|
+
ls.setItem(sshConfigLoadKey, 'yes')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const renderList = function () {
|
|
43
|
+
if (!sshConfigs.length) {
|
|
44
|
+
return (
|
|
45
|
+
<Empty />
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
return sshConfigs.map((d, i) => {
|
|
49
|
+
return (
|
|
50
|
+
<SshConfigItem item={d} key={d.title} />
|
|
51
|
+
)
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (sshConfigs.length && ls.getItem(sshConfigLoadKey) !== 'yes') {
|
|
57
|
+
loadSshConfig()
|
|
58
|
+
}
|
|
59
|
+
}, [sshConfigs.length])
|
|
60
|
+
if (!showSshConfigModal) {
|
|
61
|
+
return null
|
|
62
|
+
}
|
|
63
|
+
const modProps = {
|
|
64
|
+
title: e('loadSshConfigs'),
|
|
65
|
+
footer: null,
|
|
66
|
+
open: true,
|
|
67
|
+
onCancel: handleCancel,
|
|
68
|
+
width: '80%'
|
|
69
|
+
}
|
|
70
|
+
return (
|
|
71
|
+
<Modal {...modProps}>
|
|
72
|
+
<Spin spinning={loading}>
|
|
73
|
+
<div className='pd1y'>
|
|
74
|
+
<Button
|
|
75
|
+
onClick={loadSshConfig}
|
|
76
|
+
disabled={loading}
|
|
77
|
+
className='mg1b'
|
|
78
|
+
>
|
|
79
|
+
<ReloadOutlined /> {e('reload')}
|
|
80
|
+
</Button>
|
|
81
|
+
</div>
|
|
82
|
+
<div className='ssh-config-list'>
|
|
83
|
+
{
|
|
84
|
+
renderList()
|
|
85
|
+
}
|
|
86
|
+
</div>
|
|
87
|
+
<div className='pd1y'>
|
|
88
|
+
<Button
|
|
89
|
+
type='primary'
|
|
90
|
+
className='mg1r mg1b'
|
|
91
|
+
onClick={handleLoadSshConfig}
|
|
92
|
+
disabled={!sshConfigs.length || loading}
|
|
93
|
+
>
|
|
94
|
+
{e('import')}
|
|
95
|
+
</Button>
|
|
96
|
+
<Button
|
|
97
|
+
onClick={handleCancel}
|
|
98
|
+
className='mg1r mg1b'
|
|
99
|
+
>
|
|
100
|
+
{e('cancel')}
|
|
101
|
+
</Button>
|
|
102
|
+
</div>
|
|
103
|
+
</Spin>
|
|
104
|
+
</Modal>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Tooltip } from 'antd'
|
|
2
|
+
|
|
3
|
+
export default function SshConfigItem (props) {
|
|
4
|
+
const { item } = props
|
|
5
|
+
|
|
6
|
+
const generateTooltipContent = (item) => {
|
|
7
|
+
return Object.entries(item)
|
|
8
|
+
.filter(([key]) => key !== 'id')
|
|
9
|
+
.map(([key, value]) => (
|
|
10
|
+
<div key={key}>
|
|
11
|
+
<b className='mg1r'>{key}:</b>
|
|
12
|
+
<span>{value}</span>
|
|
13
|
+
</div>
|
|
14
|
+
))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Tooltip title={generateTooltipContent(item)}>
|
|
19
|
+
<div>
|
|
20
|
+
<div className='elli pd1y pd2x'>
|
|
21
|
+
ssh {item.title}
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</Tooltip>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React, { useEffect } from 'react'
|
|
2
|
+
import { notification, Button } from 'antd'
|
|
3
|
+
import * as ls from '../../common/safe-local-storage'
|
|
4
|
+
import {
|
|
5
|
+
sshConfigKey,
|
|
6
|
+
sshConfigLoadKey
|
|
7
|
+
} from '../../common/constants'
|
|
8
|
+
|
|
9
|
+
const e = window.translate
|
|
10
|
+
|
|
11
|
+
function handleLoad () {
|
|
12
|
+
window.store.showSshConfigModal = true
|
|
13
|
+
notification.destroy('sshConfigNotify')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function handleIgnore () {
|
|
17
|
+
ls.setItem(sshConfigKey, 'yes')
|
|
18
|
+
notification.destroy('sshConfigNotify')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function showNotification () {
|
|
22
|
+
notification.info({
|
|
23
|
+
message: e('loadSshConfigs'),
|
|
24
|
+
duration: 0,
|
|
25
|
+
placement: 'bottom',
|
|
26
|
+
key: 'sshConfigNotify',
|
|
27
|
+
description: (
|
|
28
|
+
<div>
|
|
29
|
+
<p>{e('sshConfigNotice')}</p>
|
|
30
|
+
<Button type='primary' onClick={handleLoad} className='mg1r mg1b'>
|
|
31
|
+
{e('import')}
|
|
32
|
+
</Button>
|
|
33
|
+
<Button onClick={handleIgnore} className='mg1r mg1b'>
|
|
34
|
+
{e('ignore')}
|
|
35
|
+
</Button>
|
|
36
|
+
</div>
|
|
37
|
+
)
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default function SshConfigLoadNotify (props) {
|
|
42
|
+
const { settingTab, showModal, sshConfigs } = props
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
const ignoreSshConfig = ls.getItem(sshConfigKey)
|
|
46
|
+
const sshConfigLoaded = ls.getItem(sshConfigLoadKey)
|
|
47
|
+
const shouldShow =
|
|
48
|
+
sshConfigs.length &&
|
|
49
|
+
ignoreSshConfig !== 'yes' &&
|
|
50
|
+
settingTab === 'bookmarks' &&
|
|
51
|
+
showModal &&
|
|
52
|
+
sshConfigLoaded !== 'yes'
|
|
53
|
+
|
|
54
|
+
if (shouldShow) {
|
|
55
|
+
showNotification()
|
|
56
|
+
}
|
|
57
|
+
}, [settingTab, showModal, sshConfigs.length])
|
|
58
|
+
|
|
59
|
+
return null
|
|
60
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import React from 'react'
|
|
6
6
|
import runIdle from '../../common/run-idle'
|
|
7
|
-
import {
|
|
7
|
+
import { debounce } from 'lodash-es'
|
|
8
8
|
import TabTitle from './tab-title'
|
|
9
9
|
import {
|
|
10
10
|
CodeFilled,
|
|
@@ -227,13 +227,13 @@ export default class Tabs extends React.Component {
|
|
|
227
227
|
|
|
228
228
|
adjustScroll = () => {
|
|
229
229
|
const { tabs, currentBatchTabId, batch } = this.props
|
|
230
|
-
const index = findIndex(
|
|
230
|
+
const index = tabs.findIndex(t => t.id === currentBatchTabId)
|
|
231
231
|
const tabsDomWith = Array.from(
|
|
232
232
|
document.querySelectorAll(`.v${batch + 1} .tab`)
|
|
233
|
-
).slice(0, index +
|
|
233
|
+
).slice(0, index + 1).reduce((prev, c) => {
|
|
234
234
|
return prev + c.clientWidth
|
|
235
235
|
}, 0)
|
|
236
|
-
const w = (index + 1) * tabMargin +
|
|
236
|
+
const w = (index + 1) * tabMargin + tabsDomWith
|
|
237
237
|
const tabsInnerWidth = this.getInnerWidth()
|
|
238
238
|
const scrollLeft = w > tabsInnerWidth
|
|
239
239
|
? w - tabsInnerWidth
|
|
@@ -10,13 +10,12 @@ import {
|
|
|
10
10
|
} from '@ant-design/icons'
|
|
11
11
|
import { Tooltip, message } from 'antd'
|
|
12
12
|
import classnames from 'classnames'
|
|
13
|
-
import {
|
|
13
|
+
import { pick } from 'lodash-es'
|
|
14
14
|
import Input from '../common/input-auto-focus'
|
|
15
15
|
import createName from '../../common/create-title'
|
|
16
16
|
import { addClass, removeClass } from '../../common/class'
|
|
17
|
-
import
|
|
18
|
-
|
|
19
|
-
} from '../../common/constants'
|
|
17
|
+
import isDark from '../../common/is-color-dark'
|
|
18
|
+
import { action } from 'manate'
|
|
20
19
|
import { shortcutDescExtend } from '../shortcuts/shortcut-handler.js'
|
|
21
20
|
|
|
22
21
|
const e = window.translate
|
|
@@ -117,7 +116,13 @@ class Tab extends Component {
|
|
|
117
116
|
}
|
|
118
117
|
|
|
119
118
|
const fromTab = JSON.parse(e.dataTransfer.getData('fromFile'))
|
|
120
|
-
|
|
119
|
+
let onDropTab = target
|
|
120
|
+
while (onDropTab) {
|
|
121
|
+
if (onDropTab.classList && onDropTab.classList.contains('tab')) {
|
|
122
|
+
break
|
|
123
|
+
}
|
|
124
|
+
onDropTab = onDropTab.parentElement
|
|
125
|
+
}
|
|
121
126
|
if (!onDropTab || !fromTab) {
|
|
122
127
|
return
|
|
123
128
|
}
|
|
@@ -129,8 +134,8 @@ class Tab extends Component {
|
|
|
129
134
|
|
|
130
135
|
const { id } = fromTab
|
|
131
136
|
const storeTabs = window.store.tabs
|
|
132
|
-
const indexFrom = findIndex(
|
|
133
|
-
let indexDrop = findIndex(
|
|
137
|
+
const indexFrom = storeTabs.findIndex(t => t.id === id)
|
|
138
|
+
let indexDrop = storeTabs.findIndex(t => t.id === dropId)
|
|
134
139
|
|
|
135
140
|
if (indexFrom >= 0 && indexDrop >= 0) {
|
|
136
141
|
const targetTab = storeTabs[indexDrop]
|
|
@@ -146,12 +151,14 @@ class Tab extends Component {
|
|
|
146
151
|
}
|
|
147
152
|
|
|
148
153
|
// Reorder tabs and update batch
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
154
|
+
action(function () {
|
|
155
|
+
const [tab] = storeTabs.splice(indexFrom, 1)
|
|
156
|
+
tab.batch = targetTab.batch // Update the batch to match target tab's batch
|
|
157
|
+
if (indexFrom < indexDrop) {
|
|
158
|
+
indexDrop = indexDrop - 1
|
|
159
|
+
}
|
|
160
|
+
storeTabs.splice(indexDrop, 0, tab)
|
|
161
|
+
})()
|
|
155
162
|
window.store.focus()
|
|
156
163
|
}
|
|
157
164
|
}
|
|
@@ -218,11 +225,10 @@ class Tab extends Component {
|
|
|
218
225
|
}
|
|
219
226
|
|
|
220
227
|
renderContext = () => {
|
|
221
|
-
const { tabs,
|
|
228
|
+
const { tabs, tabIndex } = this.props
|
|
222
229
|
const len = tabs.length
|
|
223
230
|
const index = tabIndex
|
|
224
231
|
const noRight = index >= len - 1
|
|
225
|
-
const isSshConfig = tab.type === terminalSshConfigType
|
|
226
232
|
const res = []
|
|
227
233
|
const reloadShortcut = this.getShortcut('app_reloadCurrentTab')
|
|
228
234
|
const closeShortcut = this.getShortcut('app_closeCurrentTab')
|
|
@@ -262,7 +268,6 @@ class Tab extends Component {
|
|
|
262
268
|
subText: cloneToNextShortcut
|
|
263
269
|
})
|
|
264
270
|
res.push({
|
|
265
|
-
disabled: isSshConfig,
|
|
266
271
|
func: 'doRename',
|
|
267
272
|
icon: 'EditOutlined',
|
|
268
273
|
text: e('rename')
|
|
@@ -380,9 +385,12 @@ class Tab extends Component {
|
|
|
380
385
|
if (isEditting) {
|
|
381
386
|
return this.renderEditting(tab, cls)
|
|
382
387
|
}
|
|
383
|
-
const { tabCount, color } = tab
|
|
388
|
+
const { tabCount, color = '#0088cc' } = tab
|
|
384
389
|
const styleTag = color
|
|
385
|
-
? {
|
|
390
|
+
? {
|
|
391
|
+
background: color,
|
|
392
|
+
color: isDark(color) ? '#fff' : '#000'
|
|
393
|
+
}
|
|
386
394
|
: {}
|
|
387
395
|
return (
|
|
388
396
|
<Tooltip
|
|
@@ -409,7 +417,6 @@ class Tab extends Component {
|
|
|
409
417
|
className='tab-title elli pd1x'
|
|
410
418
|
onClick={this.handleClick}
|
|
411
419
|
onDoubleClick={this.handleDup}
|
|
412
|
-
style={styleTag}
|
|
413
420
|
>
|
|
414
421
|
<Loading3QuartersOutlined
|
|
415
422
|
className='pointer tab-reload mg1r'
|
|
@@ -417,7 +424,8 @@ class Tab extends Component {
|
|
|
417
424
|
title={e('reload')}
|
|
418
425
|
/>
|
|
419
426
|
<span className='tab-title'>
|
|
420
|
-
{
|
|
427
|
+
<span className='iblock mg1r tab-count' style={styleTag}>{tabCount}</span>
|
|
428
|
+
<span className='mg1r'>{title}</span>
|
|
421
429
|
</span>
|
|
422
430
|
</div>
|
|
423
431
|
<div className={'tab-status ' + status} />
|
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
paneMap,
|
|
21
21
|
typeMap,
|
|
22
22
|
isWin,
|
|
23
|
-
terminalSshConfigType,
|
|
24
23
|
transferTypeMap,
|
|
25
24
|
terminalActions,
|
|
26
25
|
commonActions,
|
|
@@ -818,8 +817,7 @@ clear\r`
|
|
|
818
817
|
}
|
|
819
818
|
|
|
820
819
|
isRemote = () => {
|
|
821
|
-
return this.props.tab?.host
|
|
822
|
-
this.props.tab?.type !== terminalSshConfigType
|
|
820
|
+
return this.props.tab?.host
|
|
823
821
|
}
|
|
824
822
|
|
|
825
823
|
onPaste = async () => {
|
|
@@ -1055,15 +1053,9 @@ clear\r`
|
|
|
1055
1053
|
runInitScript = () => {
|
|
1056
1054
|
window.store.triggerResize()
|
|
1057
1055
|
const {
|
|
1058
|
-
type,
|
|
1059
|
-
title,
|
|
1060
1056
|
startDirectory,
|
|
1061
1057
|
runScripts
|
|
1062
1058
|
} = this.props.tab
|
|
1063
|
-
if (type === terminalSshConfigType) {
|
|
1064
|
-
const cmd = `ssh ${title.split(/\s/g)[0]}\r`
|
|
1065
|
-
return this.attachAddon._sendData(cmd)
|
|
1066
|
-
}
|
|
1067
1059
|
const startFolder = startDirectory || window.initFolder
|
|
1068
1060
|
if (startFolder) {
|
|
1069
1061
|
const cmd = `cd "${startFolder}"\r`
|
|
@@ -1141,10 +1133,7 @@ clear\r`
|
|
|
1141
1133
|
id
|
|
1142
1134
|
} = tab
|
|
1143
1135
|
const { savePassword } = this.state
|
|
1144
|
-
const
|
|
1145
|
-
const termType = isSshConfig
|
|
1146
|
-
? typeMap.local
|
|
1147
|
-
: type
|
|
1136
|
+
const termType = type
|
|
1148
1137
|
const extra = this.props.sessionOptions
|
|
1149
1138
|
const opts = clone({
|
|
1150
1139
|
cols,
|
|
@@ -1174,7 +1163,7 @@ clear\r`
|
|
|
1174
1163
|
termType,
|
|
1175
1164
|
readyTimeout: config.sshReadyTimeout,
|
|
1176
1165
|
proxy: getProxy(tab, config),
|
|
1177
|
-
type: tab.host
|
|
1166
|
+
type: tab.host
|
|
1178
1167
|
? typeMap.remote
|
|
1179
1168
|
: typeMap.local
|
|
1180
1169
|
})
|
|
@@ -1326,10 +1315,7 @@ clear\r`
|
|
|
1326
1315
|
isRemote: this.isRemote(),
|
|
1327
1316
|
isActive: this.isActiveTerminal()
|
|
1328
1317
|
}
|
|
1329
|
-
window.store.
|
|
1330
|
-
'terminalInfoProps',
|
|
1331
|
-
infoProps
|
|
1332
|
-
)
|
|
1318
|
+
Object.assign(window.store.terminalInfoProps, infoProps)
|
|
1333
1319
|
}
|
|
1334
1320
|
|
|
1335
1321
|
// getPwd = async () => {
|