@electerm/electerm-react 1.51.8 → 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 +16 -1
- 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/bookmark-form/render-connection-hopping.jsx +25 -2
- package/client/components/bookmark-form/ssh-form.jsx +1 -2
- package/client/components/bookmark-form/tree-delete.jsx +5 -10
- 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/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 +23 -3
- 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 +7 -2
- package/client/components/setting-panel/setting-common.jsx +28 -7
- package/client/components/setting-panel/setting-terminal.jsx +7 -4
- package/client/components/sftp/confirm-modal-store.jsx +3 -4
- package/client/components/sftp/sftp-entry.jsx +2 -5
- package/client/components/sftp/transfer-conflict-store.jsx +1 -3
- package/client/components/sftp/transport-action-store.jsx +21 -10
- package/client/components/side-panel-r/side-panel-r.jsx +13 -40
- package/client/components/sidebar/bookmark-select.jsx +0 -3
- package/client/components/sidebar/index.jsx +1 -6
- package/client/components/sidebar/side-panel.jsx +27 -51
- package/client/components/sidebar/sidebar.styl +0 -9
- 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/tab.jsx +9 -10
- 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 +25 -32
- package/client/store/bookmark-group.js +2 -13
- package/client/store/bookmark.js +43 -1
- package/client/store/common.js +2 -8
- package/client/store/index.js +45 -50
- package/client/store/init-state.js +16 -20
- package/client/store/load-data.js +5 -10
- package/client/store/setting.js +8 -15
- package/client/store/sync.js +0 -1
- package/client/store/tab.js +22 -11
- package/client/store/terminal-theme.js +1 -1
- package/client/store/transfer-list.js +0 -8
- package/client/store/ui-theme.js +0 -9
- package/client/store/watch.js +8 -8
- package/package.json +1 -1
|
@@ -38,7 +38,7 @@ export const contextMenuPaddingTop = 10
|
|
|
38
38
|
export const sftpControlHeight = 42 + 30
|
|
39
39
|
export const sidebarWidth = 43
|
|
40
40
|
export const maxHistory = 50
|
|
41
|
-
export const maxTransport =
|
|
41
|
+
export const maxTransport = 1
|
|
42
42
|
export const maxSftpHistory = 20
|
|
43
43
|
export const maxZoom = 8
|
|
44
44
|
export const minZoom = 0.5
|
|
@@ -306,6 +306,7 @@ export const sshTunnelHelpLink = 'https://github.com/electerm/electerm/wiki/How-
|
|
|
306
306
|
export const batchOpHelpLink = 'https://github.com/electerm/electerm/wiki/batch-operation'
|
|
307
307
|
export const proxyHelpLink = 'https://github.com/electerm/electerm/wiki/proxy-format'
|
|
308
308
|
export const regexHelpLink = 'https://github.com/electerm/electerm/wiki/Terminal-keywords-highlight-regular-expression-exmaples'
|
|
309
|
+
export const connectionHoppingWikiLink = 'https://github.com/electerm/electerm/wiki/Connection-Hopping-Behavior-Change-in-electerm-since-v1.50.65'
|
|
309
310
|
export const modals = {
|
|
310
311
|
hide: 0,
|
|
311
312
|
setting: 1,
|
|
@@ -397,3 +398,17 @@ export const syncDataMaps = {
|
|
|
397
398
|
profiles: ['profiles'],
|
|
398
399
|
addressBookmarks: ['addressBookmarks']
|
|
399
400
|
}
|
|
401
|
+
export const terminalTypes = [
|
|
402
|
+
'xterm-256color',
|
|
403
|
+
'xterm-new',
|
|
404
|
+
'xterm-color',
|
|
405
|
+
'xterm-vt220',
|
|
406
|
+
'xterm',
|
|
407
|
+
'linux',
|
|
408
|
+
'vt100',
|
|
409
|
+
'ansi',
|
|
410
|
+
'rxvt'
|
|
411
|
+
]
|
|
412
|
+
export const sshConfigLoadKey = 'ssh-config-loaded'
|
|
413
|
+
export const sshConfigKey = 'ignore-ssh-config'
|
|
414
|
+
export const connectionHoppingWarnKey = 'connectionHoppingWarnned'
|
|
@@ -17,17 +17,21 @@ function expandShorthandColor (color) {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export default function isColorDark (_color) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
try {
|
|
21
|
+
let color = expandShorthandColor(_color)
|
|
22
|
+
if (color.charAt(0) === '#') {
|
|
23
|
+
color = color.slice(1) // Remove the '#' if present
|
|
24
|
+
}
|
|
25
|
+
const r = parseInt(color.substr(0, 2), 16)
|
|
26
|
+
const g = parseInt(color.substr(2, 2), 16)
|
|
27
|
+
const b = parseInt(color.substr(4, 2), 16)
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
// Formula to determine brightness
|
|
30
|
+
const brightness = (r * 299 + g * 587 + b * 114) / 1000
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
// Decide based on brightness threshold
|
|
33
|
+
return brightness < 128 // You can adjust this threshold as needed
|
|
34
|
+
} catch (e) {
|
|
35
|
+
return true
|
|
36
|
+
}
|
|
33
37
|
}
|
|
@@ -11,11 +11,11 @@ const e = window.translate
|
|
|
11
11
|
window.et.tabCount = 0
|
|
12
12
|
|
|
13
13
|
export function updateCount (tab) {
|
|
14
|
-
tab.tabCount = window.et.tabCount
|
|
15
14
|
window.et.tabCount++
|
|
15
|
+
return window.et.tabCount
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export default (removeTitle
|
|
18
|
+
export default (removeTitle) => {
|
|
19
19
|
const res = {
|
|
20
20
|
id: uid(),
|
|
21
21
|
status: 'processing',
|
|
@@ -25,8 +25,5 @@ export default (removeTitle, noUpdateCount) => {
|
|
|
25
25
|
if (removeTitle) {
|
|
26
26
|
delete res.title
|
|
27
27
|
}
|
|
28
|
-
if (!noUpdateCount) {
|
|
29
|
-
updateCount(res)
|
|
30
|
-
}
|
|
31
28
|
return res
|
|
32
29
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function getReverseColor (hex) {
|
|
2
|
+
// Check if the input is a valid hex color code
|
|
3
|
+
if (!/^#[0-9a-fA-F]{6}$/.test(hex)) {
|
|
4
|
+
return '#0088cc'
|
|
5
|
+
}
|
|
6
|
+
// Convert the hex color code to an integer
|
|
7
|
+
const num = parseInt(hex.slice(1), 16)
|
|
8
|
+
// Bitwise XOR the integer with 0xFFFFFF to get the reverse color
|
|
9
|
+
const reverse = num ^ 0xFFFFFF
|
|
10
|
+
// Convert the reverse color to a hex string and pad with zeros if needed
|
|
11
|
+
return '#' + reverse.toString(16).padStart(6, '0')
|
|
12
|
+
}
|
package/client/common/ws.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import generate from './uid'
|
|
6
6
|
import wait from './wait'
|
|
7
7
|
import copy from 'json-deep-copy'
|
|
8
|
+
import { pick } from 'lodash-es'
|
|
8
9
|
|
|
9
10
|
const onces = {}
|
|
10
11
|
const wss = {}
|
|
@@ -151,7 +152,9 @@ export default (type, id, sessionId = '', sftpId = '', persist) => {
|
|
|
151
152
|
id,
|
|
152
153
|
sessionId,
|
|
153
154
|
sftpId,
|
|
154
|
-
window.store.config
|
|
155
|
+
pick(window.store.config, [
|
|
156
|
+
'host', 'port', 'tokenElecterm'
|
|
157
|
+
])
|
|
155
158
|
]
|
|
156
159
|
})
|
|
157
160
|
onces[id] = {
|
|
@@ -6,7 +6,10 @@ import {
|
|
|
6
6
|
Button,
|
|
7
7
|
Table
|
|
8
8
|
} from 'antd'
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
formItemLayout,
|
|
11
|
+
tailFormItemLayout
|
|
12
|
+
} from '../../common/form-layout'
|
|
10
13
|
import {
|
|
11
14
|
MinusCircleFilled,
|
|
12
15
|
PlusOutlined
|
|
@@ -14,10 +17,13 @@ import {
|
|
|
14
17
|
import RenderAuth from './render-auth-ssh'
|
|
15
18
|
import uid from '../../common/uid'
|
|
16
19
|
import {
|
|
17
|
-
authTypeMap
|
|
20
|
+
authTypeMap,
|
|
21
|
+
connectionHoppingWarnKey
|
|
18
22
|
} from '../../common/constants'
|
|
19
23
|
import { useState } from 'react'
|
|
24
|
+
import ConnectionHoppingWarningText from '../common/connection-hopping-warning-text'
|
|
20
25
|
import BookmarkSelect from './bookmark-select'
|
|
26
|
+
import * as ls from '../../common/safe-local-storage'
|
|
21
27
|
|
|
22
28
|
const FormItem = Form.Item
|
|
23
29
|
const RadioButton = Radio.Button
|
|
@@ -35,6 +41,12 @@ export default function renderConnectionHopping (props) {
|
|
|
35
41
|
port: 22,
|
|
36
42
|
authType: authTypeMap.password
|
|
37
43
|
})
|
|
44
|
+
const [showWarn, setShowWarn] = useState(
|
|
45
|
+
window.store.hasOldConnectionHoppingBookmark && ls.getItem(connectionHoppingWarnKey) !== 'yes'
|
|
46
|
+
)
|
|
47
|
+
function closeWarn () {
|
|
48
|
+
setShowWarn(false)
|
|
49
|
+
}
|
|
38
50
|
const [list, setList] = useState(formData.connectionHoppings || [])
|
|
39
51
|
function onChangeAuthType (e) {
|
|
40
52
|
editState(old => {
|
|
@@ -139,6 +151,16 @@ export default function renderConnectionHopping (props) {
|
|
|
139
151
|
</FormItem>
|
|
140
152
|
)
|
|
141
153
|
}
|
|
154
|
+
function renderWarn () {
|
|
155
|
+
if (!showWarn) {
|
|
156
|
+
return null
|
|
157
|
+
}
|
|
158
|
+
return (
|
|
159
|
+
<FormItem {...tailFormItemLayout}>
|
|
160
|
+
<ConnectionHoppingWarningText closeWarn={closeWarn} />
|
|
161
|
+
</FormItem>
|
|
162
|
+
)
|
|
163
|
+
}
|
|
142
164
|
const treeProps = {
|
|
143
165
|
bookmarks: store.bookmarks.filter(d => {
|
|
144
166
|
return d.host && d.port && d.username
|
|
@@ -160,6 +182,7 @@ export default function renderConnectionHopping (props) {
|
|
|
160
182
|
initialValues={initialValues}
|
|
161
183
|
>
|
|
162
184
|
{renderList()}
|
|
185
|
+
{renderWarn()}
|
|
163
186
|
<FormItem
|
|
164
187
|
{...formItemLayout}
|
|
165
188
|
label={e('chooseFromBookmarks')}
|
|
@@ -197,8 +197,7 @@ export default class BookmarkForm extends PureComponent {
|
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
setNewItem = (
|
|
200
|
-
settingItem = getInitItem([],
|
|
201
|
-
settingMap.bookmarks)
|
|
200
|
+
settingItem = getInitItem([], settingMap.bookmarks)
|
|
202
201
|
) => {
|
|
203
202
|
const { store } = this.props
|
|
204
203
|
this.props.store.storeAssign({
|
|
@@ -4,22 +4,17 @@ import {
|
|
|
4
4
|
Button
|
|
5
5
|
} from 'antd'
|
|
6
6
|
import { defaultBookmarkGroupId, settingMap } from '../../common/constants'
|
|
7
|
+
import deepCopy from 'json-deep-copy'
|
|
7
8
|
|
|
8
9
|
const e = window.translate
|
|
9
10
|
|
|
10
11
|
export default class BookmarkTreeDelete extends StartSessionSelect {
|
|
11
12
|
onExpand = (expandedKeys) => {
|
|
12
|
-
window.store.
|
|
13
|
-
'expandedKeys',
|
|
14
|
-
expandedKeys
|
|
15
|
-
)
|
|
13
|
+
window.store.expandedKeys = deepCopy(expandedKeys)
|
|
16
14
|
}
|
|
17
15
|
|
|
18
16
|
onCheck = (checkedKeys) => {
|
|
19
|
-
window.store.
|
|
20
|
-
'checkedKeys',
|
|
21
|
-
checkedKeys
|
|
22
|
-
)
|
|
17
|
+
window.store.checkedKeys = deepCopy(checkedKeys)
|
|
23
18
|
}
|
|
24
19
|
|
|
25
20
|
handleDel = () => {
|
|
@@ -42,13 +37,13 @@ export default class BookmarkTreeDelete extends StartSessionSelect {
|
|
|
42
37
|
const arr = checkedKeys.filter(d => d !== defaultBookmarkGroupId)
|
|
43
38
|
store.delItems(arr, settingMap.bookmarks)
|
|
44
39
|
store.delItems(arr, settingMap.bookmarkGroups)
|
|
45
|
-
store.
|
|
40
|
+
store.checkedKeys = []
|
|
46
41
|
}
|
|
47
42
|
|
|
48
43
|
handleCancel = () => {
|
|
49
44
|
const { store } = window
|
|
50
45
|
store.bookmarkSelectMode = false
|
|
51
|
-
store.
|
|
46
|
+
store.checkedKeys = []
|
|
52
47
|
}
|
|
53
48
|
|
|
54
49
|
render () {
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
import { formItemLayout } from '../../common/form-layout'
|
|
17
17
|
import defaultSettings from '../../common/default-setting'
|
|
18
18
|
import mapper from '../../common/auto-complete-data-mapper'
|
|
19
|
-
import { defaultEnvLang } from '../../common/constants'
|
|
19
|
+
import { defaultEnvLang, terminalTypes } from '../../common/constants'
|
|
20
20
|
|
|
21
21
|
const FormItem = Form.Item
|
|
22
22
|
const e = window.translate
|
|
@@ -26,7 +26,6 @@ export default function useBookmarkFormUI (props) {
|
|
|
26
26
|
fontFamily: defaultFontFamily,
|
|
27
27
|
fontSize: defaultFontSize
|
|
28
28
|
} = defaultSettings
|
|
29
|
-
const { terminalTypes } = props.store.config
|
|
30
29
|
return [
|
|
31
30
|
<FormItem
|
|
32
31
|
{...formItemLayout}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Button
|
|
3
|
+
} from 'antd'
|
|
4
|
+
import Link from './external-link'
|
|
5
|
+
import {
|
|
6
|
+
connectionHoppingWikiLink,
|
|
7
|
+
connectionHoppingWarnKey
|
|
8
|
+
} from '../../common/constants'
|
|
9
|
+
import * as ls from '../../common/safe-local-storage'
|
|
10
|
+
|
|
11
|
+
const e = window.translate
|
|
12
|
+
|
|
13
|
+
export default function ConnectionHoppingWarningText (props) {
|
|
14
|
+
function handleRead () {
|
|
15
|
+
ls.setItem(connectionHoppingWarnKey, 'yes')
|
|
16
|
+
props.closeWarn()
|
|
17
|
+
}
|
|
18
|
+
return (
|
|
19
|
+
<div className='pd1'>
|
|
20
|
+
<div className='pd1b'>
|
|
21
|
+
<span>{e('connectionHoppingWarning')}</span>
|
|
22
|
+
</div>
|
|
23
|
+
<div className='pd1b'>
|
|
24
|
+
<Link to={connectionHoppingWikiLink}>{connectionHoppingWikiLink}</Link>
|
|
25
|
+
</div>
|
|
26
|
+
<div className='pd1b'>
|
|
27
|
+
<Button
|
|
28
|
+
onClick={handleRead}
|
|
29
|
+
size='small'
|
|
30
|
+
>
|
|
31
|
+
{e('haveRead')}
|
|
32
|
+
</Button>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useCallback, useRef, useState, memo } from 'react'
|
|
2
|
+
import './drag-handle.styl'
|
|
3
|
+
|
|
4
|
+
export default memo(function DragHandle (props) {
|
|
5
|
+
const [isDragging, setIsDragging] = useState(false)
|
|
6
|
+
const dragStartRef = useRef(false)
|
|
7
|
+
const clientXRef = useRef(0)
|
|
8
|
+
const {
|
|
9
|
+
max,
|
|
10
|
+
min,
|
|
11
|
+
width,
|
|
12
|
+
left = true
|
|
13
|
+
} = props
|
|
14
|
+
|
|
15
|
+
const calc = useCallback((clientX) => {
|
|
16
|
+
let nw = left
|
|
17
|
+
? clientX - clientXRef.current + width
|
|
18
|
+
: clientXRef.current - clientX + width
|
|
19
|
+
if (nw < min) {
|
|
20
|
+
nw = min
|
|
21
|
+
} else if (nw > max) {
|
|
22
|
+
nw = max
|
|
23
|
+
}
|
|
24
|
+
return nw
|
|
25
|
+
}, [props.max, props.min, props.left])
|
|
26
|
+
|
|
27
|
+
const handleMousedown = useCallback((e) => {
|
|
28
|
+
e.stopPropagation()
|
|
29
|
+
setIsDragging(true)
|
|
30
|
+
dragStartRef.current = true
|
|
31
|
+
clientXRef.current = e.clientX
|
|
32
|
+
window.addEventListener('mouseup', handleMouseup)
|
|
33
|
+
window.addEventListener('mousemove', handleMousemove)
|
|
34
|
+
}, [])
|
|
35
|
+
|
|
36
|
+
const handleMouseup = useCallback((e) => {
|
|
37
|
+
setIsDragging(false)
|
|
38
|
+
dragStartRef.current = false
|
|
39
|
+
const nw = calc(e.clientX)
|
|
40
|
+
props.onDragEnd(nw)
|
|
41
|
+
window.store.onResize()
|
|
42
|
+
window.removeEventListener('mouseup', handleMouseup)
|
|
43
|
+
window.removeEventListener('mousemove', handleMousemove)
|
|
44
|
+
}, [])
|
|
45
|
+
|
|
46
|
+
const handleMousemove = useCallback((e) => {
|
|
47
|
+
const nw = calc(e.clientX)
|
|
48
|
+
props.onDragMove(nw)
|
|
49
|
+
}, [width])
|
|
50
|
+
const divProps = {
|
|
51
|
+
className: 'drag-handle' + (isDragging ? ' dragging' : ''),
|
|
52
|
+
onMouseDown: handleMousedown,
|
|
53
|
+
draggable: false
|
|
54
|
+
}
|
|
55
|
+
return (
|
|
56
|
+
<div
|
|
57
|
+
{...divProps}
|
|
58
|
+
/>
|
|
59
|
+
)
|
|
60
|
+
})
|
|
@@ -40,9 +40,10 @@ export default auto(function Layout (props) {
|
|
|
40
40
|
rightPanelVisible,
|
|
41
41
|
rightPanelPinned,
|
|
42
42
|
rightPanelWidth,
|
|
43
|
-
resizeTrigger
|
|
43
|
+
resizeTrigger,
|
|
44
|
+
inActiveTerminal
|
|
44
45
|
} = props.store
|
|
45
|
-
const h = height - footerHeight - (pinnedQuickCommandBar ? quickCommandBoxHeight : 0) + resizeTrigger
|
|
46
|
+
const h = height - footerHeight - (inActiveTerminal && pinnedQuickCommandBar ? quickCommandBoxHeight : 0) + resizeTrigger
|
|
46
47
|
const l = pinned ? 43 + leftSidebarWidth : 43
|
|
47
48
|
const r = rightPanelVisible && rightPanelPinned ? rightPanelWidth : 0
|
|
48
49
|
return {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useEffect } from 'react'
|
|
2
|
+
import ConnectionHoppingWarningText from '../common/connection-hopping-warning-text'
|
|
3
|
+
import {
|
|
4
|
+
notification
|
|
5
|
+
} from 'antd'
|
|
6
|
+
import * as ls from '../../common/safe-local-storage'
|
|
7
|
+
import {
|
|
8
|
+
connectionHoppingWarnKey
|
|
9
|
+
} from '../../common/constants'
|
|
10
|
+
|
|
11
|
+
const e = window.translate
|
|
12
|
+
|
|
13
|
+
export default function ConnectionHoppingWarning (props) {
|
|
14
|
+
const {
|
|
15
|
+
hasOldConnectionHoppingBookmark,
|
|
16
|
+
configLoaded
|
|
17
|
+
} = props
|
|
18
|
+
function closeWarn () {
|
|
19
|
+
notification.destroy(connectionHoppingWarnKey)
|
|
20
|
+
}
|
|
21
|
+
function showWarning () {
|
|
22
|
+
if (
|
|
23
|
+
!hasOldConnectionHoppingBookmark ||
|
|
24
|
+
!configLoaded ||
|
|
25
|
+
ls.getItem(connectionHoppingWarnKey) === 'yes'
|
|
26
|
+
) {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
notification.info({
|
|
30
|
+
message: e('connectionHopping'),
|
|
31
|
+
duration: 0,
|
|
32
|
+
placement: 'bottom',
|
|
33
|
+
key: connectionHoppingWarnKey,
|
|
34
|
+
description: (
|
|
35
|
+
<ConnectionHoppingWarningText
|
|
36
|
+
closeWarn={closeWarn}
|
|
37
|
+
/>
|
|
38
|
+
)
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
showWarning()
|
|
43
|
+
}, [configLoaded, hasOldConnectionHoppingBookmark])
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
@@ -1,11 +1,35 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { FrownOutlined, ReloadOutlined } from '@ant-design/icons'
|
|
3
|
-
import { Button } from 'antd'
|
|
3
|
+
import { Button, message } from 'antd'
|
|
4
4
|
import {
|
|
5
|
-
logoPath1
|
|
5
|
+
logoPath1,
|
|
6
|
+
packInfo,
|
|
7
|
+
isMac,
|
|
8
|
+
isWin
|
|
6
9
|
} from '../../common/constants'
|
|
10
|
+
import Link from '../common/external-link'
|
|
11
|
+
import fs from '../../common/fs'
|
|
12
|
+
import { copy } from '../../common/clipboard'
|
|
7
13
|
|
|
8
14
|
const e = window.translate
|
|
15
|
+
const os = isMac ? 'mac' : isWin ? 'windows' : 'linux'
|
|
16
|
+
const troubleshootContent = {
|
|
17
|
+
runInCommandLine: {
|
|
18
|
+
mac: '/Applications/electerm.app/Contents/MacOS/electerm',
|
|
19
|
+
linux: 'path/to/electerm',
|
|
20
|
+
windows: 'path\\to\\electerm.exe'
|
|
21
|
+
},
|
|
22
|
+
clearConfig: {
|
|
23
|
+
mac: 'rm -rf ~/Library/Application\\ Support/electerm/users/default_user/electerm.data.nedb',
|
|
24
|
+
linux: 'rm -rf ~/.config/electerm/users/default_user/electerm.data.nedb',
|
|
25
|
+
windows: 'Delete C:\\Users\\your-user-name\\AppData\\Roaming\\electerm\\users\\default_user\\electerm.data.nedb'
|
|
26
|
+
},
|
|
27
|
+
clearData: {
|
|
28
|
+
mac: 'rm -rf ~/Library/Application\\ Support/electerm*',
|
|
29
|
+
linux: 'rm -rf ~/.config/electerm',
|
|
30
|
+
windows: 'Delete C:\\Users\\your-user-name\\AppData\\Roaming\\electerm'
|
|
31
|
+
}
|
|
32
|
+
}
|
|
9
33
|
|
|
10
34
|
export default class ErrorBoundary extends React.PureComponent {
|
|
11
35
|
constructor (props) {
|
|
@@ -28,12 +52,101 @@ export default class ErrorBoundary extends React.PureComponent {
|
|
|
28
52
|
window.location.reload()
|
|
29
53
|
}
|
|
30
54
|
|
|
55
|
+
handleClearData = async () => {
|
|
56
|
+
await fs.rmrf(troubleshootContent.clearData[os])
|
|
57
|
+
.then(
|
|
58
|
+
() => {
|
|
59
|
+
message.success('Data cleared')
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
handleClearConfig = async () => {
|
|
65
|
+
await fs.rmrf(troubleshootContent.clearConfig[os])
|
|
66
|
+
.then(
|
|
67
|
+
() => {
|
|
68
|
+
message.success('Config cleared')
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
handleCopy = () => {
|
|
74
|
+
copy(troubleshootContent.runInCommandLine[os])
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
renderButton = type => {
|
|
78
|
+
if (type === 'clearData') {
|
|
79
|
+
return (
|
|
80
|
+
<Button
|
|
81
|
+
className='mg1l'
|
|
82
|
+
onClick={this.handleClearData}
|
|
83
|
+
>
|
|
84
|
+
{e('clearData')}
|
|
85
|
+
</Button>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
if (type === 'runInCommandLine') {
|
|
89
|
+
return (
|
|
90
|
+
<Button
|
|
91
|
+
className='mg1l'
|
|
92
|
+
onClick={this.handleCopy}
|
|
93
|
+
>
|
|
94
|
+
{e('copy')}
|
|
95
|
+
</Button>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
return (
|
|
99
|
+
<Button
|
|
100
|
+
className='mg1l'
|
|
101
|
+
onClick={this.handleClearConfig}
|
|
102
|
+
>
|
|
103
|
+
{e('clearConfig')}
|
|
104
|
+
</Button>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
renderTroubleShoot = () => {
|
|
109
|
+
const {
|
|
110
|
+
bugs: {
|
|
111
|
+
url: bugReportLink
|
|
112
|
+
}
|
|
113
|
+
} = packInfo
|
|
114
|
+
const bugUrl = `${bugReportLink}/new/choose`
|
|
115
|
+
return (
|
|
116
|
+
<div className='pd1y wordbreak'>
|
|
117
|
+
<h2>{e('troubleShoot')}</h2>
|
|
118
|
+
<p>Electerm Version: {packInfo.version}, OS: {os}</p>
|
|
119
|
+
{
|
|
120
|
+
Object.keys(troubleshootContent).map((k, i) => {
|
|
121
|
+
const v = troubleshootContent[k]
|
|
122
|
+
const cmd = v[os]
|
|
123
|
+
return (
|
|
124
|
+
<div className='pd1b' key={k}>
|
|
125
|
+
<h3>{e(k)} {this.renderButton(k)}</h3>
|
|
126
|
+
<p><code>{cmd}</code></p>
|
|
127
|
+
</div>
|
|
128
|
+
)
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
<div className='pd1b'>
|
|
132
|
+
<Link to={bugUrl}>{e('bugReport')}</Link>
|
|
133
|
+
</div>
|
|
134
|
+
<div className='pd3y'>
|
|
135
|
+
<img
|
|
136
|
+
src='https://electerm.html5beta.com/electerm-wechat-group-qr.jpg'
|
|
137
|
+
className='mwm-100'
|
|
138
|
+
/>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
31
144
|
render () {
|
|
32
145
|
if (this.state.hasError) {
|
|
33
146
|
const { stack, message } = this.state.error
|
|
34
147
|
return (
|
|
35
|
-
<div className='pd3
|
|
36
|
-
<div className='pd2y
|
|
148
|
+
<div className='pd3 error-wrapper'>
|
|
149
|
+
<div className='pd2y'>
|
|
37
150
|
<img src={logoPath1} className='iblock mwm-100' />
|
|
38
151
|
</div>
|
|
39
152
|
<h1>
|
|
@@ -41,7 +154,6 @@ export default class ErrorBoundary extends React.PureComponent {
|
|
|
41
154
|
<span className='iblock mg1r'>{e('error')}</span>
|
|
42
155
|
<Button
|
|
43
156
|
onClick={this.handleReload}
|
|
44
|
-
className='iblock'
|
|
45
157
|
icon={<ReloadOutlined />}
|
|
46
158
|
>
|
|
47
159
|
{e('reload')}
|
|
@@ -49,6 +161,9 @@ export default class ErrorBoundary extends React.PureComponent {
|
|
|
49
161
|
</h1>
|
|
50
162
|
<div className='pd1y'>{message}</div>
|
|
51
163
|
<div className='pd1y'>{stack}</div>
|
|
164
|
+
{
|
|
165
|
+
this.renderTroubleShoot()
|
|
166
|
+
}
|
|
52
167
|
</div>
|
|
53
168
|
)
|
|
54
169
|
}
|
|
@@ -26,6 +26,9 @@ import { LoadingUI } from './loading'
|
|
|
26
26
|
import { ConfigProvider, notification, message } from 'antd'
|
|
27
27
|
import InfoModal from '../sidebar/info-modal.jsx'
|
|
28
28
|
import RightSidePanel from '../side-panel-r/side-panel-r'
|
|
29
|
+
import ConnectionHoppingWarning from './connection-hopping-warnning'
|
|
30
|
+
import SshConfigLoadNotify from '../ssh-config/ssh-config-load-notify'
|
|
31
|
+
import LoadSshConfigs from '../ssh-config/load-ssh-configs'
|
|
29
32
|
import { pick } from 'lodash-es'
|
|
30
33
|
import deepCopy from 'json-deep-copy'
|
|
31
34
|
import './wrapper.styl'
|
|
@@ -90,7 +93,6 @@ export default auto(function Index (props) {
|
|
|
90
93
|
isSecondInstance,
|
|
91
94
|
pinnedQuickCommandBar,
|
|
92
95
|
wsInited,
|
|
93
|
-
upgradeInfo,
|
|
94
96
|
installSrc,
|
|
95
97
|
fileTransfers,
|
|
96
98
|
uiThemeConfig,
|
|
@@ -98,6 +100,7 @@ export default auto(function Index (props) {
|
|
|
98
100
|
transferToConfirm,
|
|
99
101
|
openResolutionEdit
|
|
100
102
|
} = store
|
|
103
|
+
const upgradeInfo = deepCopy(store.upgradeInfo)
|
|
101
104
|
const cls = classnames({
|
|
102
105
|
loaded: configLoaded,
|
|
103
106
|
'not-webapp': !window.et.isWebApp,
|
|
@@ -174,7 +177,7 @@ export default auto(function Index (props) {
|
|
|
174
177
|
innerWidth: store.innerWidth
|
|
175
178
|
}
|
|
176
179
|
const resProps = {
|
|
177
|
-
resolutions: store.resolutions,
|
|
180
|
+
resolutions: deepCopy(store.resolutions),
|
|
178
181
|
openResolutionEdit
|
|
179
182
|
}
|
|
180
183
|
const contextMenuProps = {
|
|
@@ -190,7 +193,7 @@ export default auto(function Index (props) {
|
|
|
190
193
|
rightPanelWidth: store.rightPanelWidth
|
|
191
194
|
}
|
|
192
195
|
const terminalInfoProps = {
|
|
193
|
-
...store.terminalInfoProps,
|
|
196
|
+
...deepCopy(store.terminalInfoProps),
|
|
194
197
|
...pick(
|
|
195
198
|
config,
|
|
196
199
|
['host', 'port', 'saveTerminalLogToFile', 'terminalInfos']
|
|
@@ -199,6 +202,17 @@ export default auto(function Index (props) {
|
|
|
199
202
|
'appPath'
|
|
200
203
|
])
|
|
201
204
|
}
|
|
205
|
+
const sshConfigProps = {
|
|
206
|
+
...pick(store, [
|
|
207
|
+
'settingTab',
|
|
208
|
+
'showModal',
|
|
209
|
+
'sshConfigs'
|
|
210
|
+
])
|
|
211
|
+
}
|
|
212
|
+
const warningProps = {
|
|
213
|
+
hasOldConnectionHoppingBookmark: store.hasOldConnectionHoppingBookmark,
|
|
214
|
+
configLoaded
|
|
215
|
+
}
|
|
202
216
|
return (
|
|
203
217
|
<ConfigProvider
|
|
204
218
|
theme={uiThemeConfig}
|
|
@@ -257,6 +271,12 @@ export default auto(function Index (props) {
|
|
|
257
271
|
<RightSidePanel {...rightPanelProps}>
|
|
258
272
|
<TerminalInfo {...terminalInfoProps} />
|
|
259
273
|
</RightSidePanel>
|
|
274
|
+
<SshConfigLoadNotify {...sshConfigProps} />
|
|
275
|
+
<LoadSshConfigs
|
|
276
|
+
showSshConfigModal={store.showSshConfigModal}
|
|
277
|
+
sshConfigs={store.sshConfigs}
|
|
278
|
+
/>
|
|
279
|
+
<ConnectionHoppingWarning {...warningProps} />
|
|
260
280
|
</div>
|
|
261
281
|
</ConfigProvider>
|
|
262
282
|
)
|