@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.
Files changed (56) hide show
  1. package/client/common/constants.js +16 -1
  2. package/client/common/default-setting.js +0 -1
  3. package/client/common/is-color-dark.js +15 -11
  4. package/client/common/new-terminal.js +2 -5
  5. package/client/common/reverse-color.js +12 -0
  6. package/client/common/ws.js +4 -1
  7. package/client/components/bookmark-form/render-connection-hopping.jsx +25 -2
  8. package/client/components/bookmark-form/ssh-form.jsx +1 -2
  9. package/client/components/bookmark-form/tree-delete.jsx +5 -10
  10. package/client/components/bookmark-form/use-ui.jsx +1 -2
  11. package/client/components/common/connection-hopping-warning-text.jsx +36 -0
  12. package/client/components/common/drag-handle.jsx +60 -0
  13. package/client/components/common/drag-handle.styl +12 -0
  14. package/client/components/layout/layout.jsx +3 -2
  15. package/client/components/main/connection-hopping-warnning.jsx +45 -0
  16. package/client/components/main/error-wrapper.jsx +120 -5
  17. package/client/components/main/main.jsx +23 -3
  18. package/client/components/main/upgrade.jsx +4 -9
  19. package/client/components/main/wrapper.styl +2 -1
  20. package/client/components/profile/profile-form-ssh.jsx +1 -1
  21. package/client/components/rdp/resolution-edit.jsx +3 -5
  22. package/client/components/session/session.jsx +7 -2
  23. package/client/components/setting-panel/setting-common.jsx +28 -7
  24. package/client/components/setting-panel/setting-terminal.jsx +7 -4
  25. package/client/components/sftp/confirm-modal-store.jsx +3 -4
  26. package/client/components/sftp/sftp-entry.jsx +2 -5
  27. package/client/components/sftp/transfer-conflict-store.jsx +1 -3
  28. package/client/components/sftp/transport-action-store.jsx +21 -10
  29. package/client/components/side-panel-r/side-panel-r.jsx +13 -40
  30. package/client/components/sidebar/bookmark-select.jsx +0 -3
  31. package/client/components/sidebar/index.jsx +1 -6
  32. package/client/components/sidebar/side-panel.jsx +27 -51
  33. package/client/components/sidebar/sidebar.styl +0 -9
  34. package/client/components/ssh-config/load-ssh-configs.jsx +106 -0
  35. package/client/components/ssh-config/ssh-config-item.jsx +26 -0
  36. package/client/components/ssh-config/ssh-config-load-notify.jsx +60 -0
  37. package/client/components/tabs/tab.jsx +9 -10
  38. package/client/components/tabs/tabs.styl +6 -1
  39. package/client/components/terminal/index.jsx +4 -18
  40. package/client/components/tree-list/bookmark-toolbar.jsx +203 -0
  41. package/client/components/tree-list/bookmark-transport.jsx +2 -0
  42. package/client/components/tree-list/tree-list.jsx +25 -32
  43. package/client/store/bookmark-group.js +2 -13
  44. package/client/store/bookmark.js +43 -1
  45. package/client/store/common.js +2 -8
  46. package/client/store/index.js +45 -50
  47. package/client/store/init-state.js +16 -20
  48. package/client/store/load-data.js +5 -10
  49. package/client/store/setting.js +8 -15
  50. package/client/store/sync.js +0 -1
  51. package/client/store/tab.js +22 -11
  52. package/client/store/terminal-theme.js +1 -1
  53. package/client/store/transfer-list.js +0 -8
  54. package/client/store/ui-theme.js +0 -9
  55. package/client/store/watch.js +8 -8
  56. 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 = 5
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'
@@ -39,7 +39,6 @@ export default {
39
39
  initDefaultTabOnStart: true,
40
40
  screenReaderMode: false,
41
41
  autoRefreshWhenSwitchToSftp: false,
42
- hideSshConfig: false,
43
42
  addTimeStampToTermLog: false,
44
43
  sftpPathFollowSsh: false,
45
44
  keepaliveInterval: 0,
@@ -17,17 +17,21 @@ function expandShorthandColor (color) {
17
17
  }
18
18
 
19
19
  export default function isColorDark (_color) {
20
- let color = expandShorthandColor(_color)
21
- if (color.charAt(0) === '#') {
22
- color = color.slice(1) // Remove the '#' if present
23
- }
24
- const r = parseInt(color.substr(0, 2), 16)
25
- const g = parseInt(color.substr(2, 2), 16)
26
- const b = parseInt(color.substr(4, 2), 16)
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
- // Formula to determine brightness
29
- const brightness = (r * 299 + g * 587 + b * 114) / 1000
29
+ // Formula to determine brightness
30
+ const brightness = (r * 299 + g * 587 + b * 114) / 1000
30
31
 
31
- // Decide based on brightness threshold
32
- return brightness < 128 // You can adjust this threshold as needed
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, noUpdateCount) => {
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
+ }
@@ -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 { formItemLayout, tailFormItemLayout } from '../../common/form-layout'
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.setState(
13
- 'expandedKeys',
14
- expandedKeys
15
- )
13
+ window.store.expandedKeys = deepCopy(expandedKeys)
16
14
  }
17
15
 
18
16
  onCheck = (checkedKeys) => {
19
- window.store.setState(
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.setState('checkedKeys', [])
40
+ store.checkedKeys = []
46
41
  }
47
42
 
48
43
  handleCancel = () => {
49
44
  const { store } = window
50
45
  store.bookmarkSelectMode = false
51
- store.setState('checkedKeys', [])
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
+ })
@@ -0,0 +1,12 @@
1
+
2
+ .drag-handle
3
+ position absolute
4
+ right 0
5
+ top 0
6
+ width 3px
7
+ bottom 0
8
+ cursor ew-resize
9
+ display none
10
+ z-index 201
11
+ &.dragging
12
+ background #08c
@@ -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 aligncenter error-wrapper'>
36
- <div className='pd2y aligncenter'>
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
  )