@electerm/electerm-react 1.34.63 → 1.35.0

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 (46) hide show
  1. package/client/common/constants.js +3 -1
  2. package/client/common/key-pressed.js +1 -1
  3. package/client/common/rand-hex-color.js +28 -0
  4. package/client/common/shortcuts-defaults.js +51 -0
  5. package/client/components/batch-op/batch-op.jsx +6 -2
  6. package/client/components/bookmark-form/color-picker-item.jsx +14 -0
  7. package/client/components/bookmark-form/color-picker.jsx +90 -0
  8. package/client/components/bookmark-form/color-picker.styl +20 -0
  9. package/client/components/bookmark-form/form-ssh-common.jsx +4 -0
  10. package/client/components/bookmark-form/hex-input.jsx +39 -0
  11. package/client/components/bookmark-form/local-form-ui.jsx +1 -0
  12. package/client/components/bookmark-form/serial-form-ui.jsx +12 -8
  13. package/client/components/bookmark-form/ssh-form-ui.jsx +3 -0
  14. package/client/components/bookmark-form/telnet-form-ui.jsx +1 -0
  15. package/client/components/bookmark-form/use-ui.jsx +11 -1
  16. package/client/components/context-menu/context-menu.styl +1 -1
  17. package/client/components/context-menu/menu-btn.jsx +6 -3
  18. package/client/components/main/custom-css.jsx +28 -0
  19. package/client/components/main/main.jsx +5 -2
  20. package/client/components/session/session.jsx +11 -35
  21. package/client/components/session/sessions.jsx +15 -15
  22. package/client/components/setting-panel/setting.jsx +20 -32
  23. package/client/components/setting-panel/tab-settings.jsx +13 -10
  24. package/client/components/sftp/transfer-tag.jsx +2 -2
  25. package/client/components/sftp/transport-action.jsx +1 -0
  26. package/client/components/shortcuts/get-key-char.js +45 -0
  27. package/client/components/shortcuts/shortcut-control.jsx +63 -0
  28. package/client/components/shortcuts/shortcut-editor.jsx +194 -0
  29. package/client/components/shortcuts/shortcut-handler.js +76 -0
  30. package/client/components/shortcuts/shortcut.styl +0 -0
  31. package/client/components/shortcuts/shortcuts-defaults.js +87 -0
  32. package/client/components/shortcuts/shortcuts.jsx +166 -0
  33. package/client/components/sidebar/index.jsx +1 -1
  34. package/client/components/sidebar/transfer-history-modal.jsx +14 -2
  35. package/client/components/tabs/index.jsx +0 -25
  36. package/client/components/tabs/tab.jsx +6 -5
  37. package/client/components/terminal/index.jsx +93 -110
  38. package/client/components/terminal/term-search.jsx +9 -21
  39. package/client/components/terminal-theme/index.jsx +4 -3
  40. package/client/store/index.js +17 -2
  41. package/client/store/init-state.js +1 -8
  42. package/client/store/item.js +3 -0
  43. package/client/store/session.js +0 -22
  44. package/client/store/watch.js +3 -1
  45. package/client/views/index.pug +2 -0
  46. package/package.json +1 -1
@@ -14,8 +14,6 @@ import {
14
14
  statusMap
15
15
  } from '../../common/constants'
16
16
  import newTerm, { updateCount } from '../../common/new-terminal'
17
- import keyControlPress from '../../common/key-control-pressed'
18
- import keyPressed from '../../common/key-pressed'
19
17
  import postMsg from '../../common/post-msg'
20
18
  import TermSearch from '../terminal/term-search'
21
19
  import Footer from '../footer/footer-entry'
@@ -23,12 +21,13 @@ import QuickCommandsFooterBox from '../quick-commands/quick-commands-box'
23
21
  import LogoElem from '../common/logo-elem'
24
22
  import { Button } from 'antd'
25
23
  import toSimpleObj from '../../common/to-simple-obj'
24
+ import { shortcutExtend } from '../shortcuts/shortcut-handler.js'
26
25
 
27
26
  const { prefix } = window
28
27
  const e = prefix('tabs')
29
28
  const c = prefix('control')
30
29
 
31
- export default class Sessions extends Component {
30
+ class Sessions extends Component {
32
31
  state = {
33
32
  tabs: [],
34
33
  currentTabId: ''
@@ -40,16 +39,14 @@ export default class Sessions extends Component {
40
39
  }
41
40
 
42
41
  initShortcuts () {
43
- window.addEventListener('keydown', e => {
44
- if (keyControlPress(e) && keyPressed(e, 'w')) {
45
- e.stopPropagation()
46
- if (this.state.tabs.length > 1) {
47
- this.delTab(
48
- this.state.currentTabId
49
- )
50
- }
51
- }
52
- })
42
+ window.addEventListener('keydown', this.handleKeyboardEvent.bind(this))
43
+ }
44
+
45
+ closeCurrentTabShortcut = (e) => {
46
+ e.stopPropagation()
47
+ this.delTab(
48
+ this.state.currentTabId
49
+ )
53
50
  }
54
51
 
55
52
  watch = () => {
@@ -418,11 +415,12 @@ export default class Sessions extends Component {
418
415
  }
419
416
 
420
417
  render () {
421
- const { store } = this.props
418
+ const { store, config } = this.props
422
419
  const currentTab = this.getCurrentTab()
423
420
  const termProps = {
424
421
  currentTab,
425
- store
422
+ store,
423
+ config
426
424
  }
427
425
  return [
428
426
  this.renderTabs(),
@@ -443,3 +441,5 @@ export default class Sessions extends Component {
443
441
  ]
444
442
  }
445
443
  }
444
+
445
+ export default shortcutExtend(Sessions)
@@ -34,7 +34,6 @@ import createEditLangLink from '../../common/create-lang-edit-link'
34
34
  import mapper from '../../common/auto-complete-data-mapper'
35
35
  import StartSession from './start-session-select'
36
36
  import HelpIcon from '../common/help-icon'
37
- import fs from '../../common/fs'
38
37
  import delay from '../../common/wait.js'
39
38
  import './setting.styl'
40
39
 
@@ -186,6 +185,10 @@ export default class Setting extends Component {
186
185
  this.props.store.setTheme(id)
187
186
  }
188
187
 
188
+ handleCustomCss = (e) => {
189
+ this.onChangeValue(e.target.value, 'customCss')
190
+ }
191
+
189
192
  onChangeValue = (value, name) => {
190
193
  if (name === 'useSystemTitleBar') {
191
194
  message.info(e('useSystemTitleBarTip'), 5)
@@ -213,21 +216,18 @@ export default class Setting extends Component {
213
216
  )
214
217
  }
215
218
 
216
- saveConfig = async (_ext) => {
217
- const config = deepCopy(this.props.config)
218
- const ext = deepCopy(_ext)
219
- const update = Object.assign({}, config, deepCopy(_ext))
219
+ saveConfig = async (ext) => {
220
+ const { config } = this.props
220
221
  if (ext.hotkey && ext.hotkey !== config.hotkey) {
221
222
  const res = await window.pre.runGlobalAsync('changeHotkey', ext.hotkey)
222
223
  if (!res) {
223
224
  message.warning(e('hotkeyNotOk'))
224
- update.config.hotkey = config.hotkey
225
- ext.hotkey = config.hotkey
225
+ delete ext.hotkey
226
226
  } else {
227
227
  message.success(e('saved'))
228
228
  }
229
229
  }
230
- this.props.store.setConfig(update)
230
+ this.props.store.setConfig(ext)
231
231
  }
232
232
 
233
233
  renderOption = (m, i) => {
@@ -663,29 +663,6 @@ export default class Setting extends Component {
663
663
  )
664
664
  }
665
665
 
666
- handlePortable = async () => {
667
- const {
668
- appPath,
669
- exePath
670
- } = this.props.store
671
- const from = osResolve(appPath, 'electerm', 'users')
672
- const tar = osResolve(exePath, 'electerm', 'users')
673
- const cmd = `xcopy /E /I /Q /Y "${from}" ${tar}`
674
- const x = await fs.runWinCmd(cmd)
675
- .catch(err => {
676
- this.props.store.onError(err)
677
- return false
678
- })
679
- if (x !== false) {
680
- message.success(
681
- `${e('dataTransferedTo')}: ${tar}`
682
- )
683
- setTimeout(
684
- this.props.store.restart, 5000
685
- )
686
- }
687
- }
688
-
689
666
  renderLoginPassAfter () {
690
667
  const {
691
668
  loginPass,
@@ -750,7 +727,8 @@ export default class Setting extends Component {
750
727
  hotkey,
751
728
  language,
752
729
  rendererType,
753
- theme
730
+ theme,
731
+ customCss
754
732
  } = this.props.config
755
733
  const {
756
734
  appPath,
@@ -848,6 +826,16 @@ export default class Setting extends Component {
848
826
  }
849
827
  </Select>
850
828
  </div>
829
+
830
+ <div className='pd2b'>
831
+ <span className='inline-title mg1r'>{e('customCss')}</span>
832
+ <Input.TextArea
833
+ onChange={this.handleCustomCss}
834
+ value={customCss}
835
+ rows={3}
836
+ />
837
+ </div>
838
+
851
839
  <div className='pd2b'>
852
840
  <span className='inline-title mg1r'>{e('language')}</span>
853
841
  <Select
@@ -1,10 +1,12 @@
1
1
  import Setting from './setting'
2
2
  import SettingCol from './col'
3
3
  import SyncSetting from '../setting-sync/setting-sync'
4
+ import Shortcuts from '../shortcuts/shortcuts'
4
5
  import List from './list'
5
6
  import {
6
7
  settingMap,
7
- settingSyncId
8
+ settingSyncId,
9
+ settingShortcutsId
8
10
  } from '../../common/constants'
9
11
 
10
12
  export default function TabSettings (props) {
@@ -19,6 +21,15 @@ export default function TabSettings (props) {
19
21
  listProps,
20
22
  store
21
23
  } = props
24
+ let elem = null
25
+ const sid = settingItem.id
26
+ if (sid === settingSyncId) {
27
+ elem = <SyncSetting store={store} />
28
+ } else if (sid === settingShortcutsId) {
29
+ elem = <Shortcuts store={store} />
30
+ } else {
31
+ elem = <Setting {...listProps} config={store.config} />
32
+ }
22
33
  return (
23
34
  <div
24
35
  className='setting-tabs-setting'
@@ -27,15 +38,7 @@ export default function TabSettings (props) {
27
38
  <List
28
39
  {...listProps}
29
40
  />
30
- {
31
- settingItem.id === settingSyncId
32
- ? (
33
- <SyncSetting
34
- store={store}
35
- />
36
- )
37
- : <Setting {...listProps} config={store.config} />
38
- }
41
+ {elem}
39
42
  </SettingCol>
40
43
  </div>
41
44
  )
@@ -21,7 +21,7 @@ export default function TransferTag (props) {
21
21
  const typeFromTitle = e(typeFrom)
22
22
  const typeToTitle = e(typeTo)
23
23
  const title = error
24
- ? `error: ${error}`
24
+ ? `[error: ${error}]`
25
25
  : ''
26
26
  return (
27
27
  <span className={'flex-child transfer-tag transfer-status-' + tagStatus} title={title}>
@@ -34,7 +34,7 @@ export default function TransferTag (props) {
34
34
  <span className='sftp-transfer-type'>
35
35
  {typeToTitle}
36
36
  </span>
37
- <span className='mg1l'>[{title}]</span>
37
+ <span className='mg1l'>{title}</span>
38
38
  </span>
39
39
  )
40
40
  }
@@ -222,6 +222,7 @@ export default function transportAction (props) {
222
222
  fromPathReal: transfer.fromPath,
223
223
  toPath: nTo,
224
224
  fromPath: p,
225
+ originalId: transfer.id,
225
226
  id: generate()
226
227
  }
227
228
  delete newTrans1.fromFile
@@ -0,0 +1,45 @@
1
+ export function getKeyCharacter (code = '') {
2
+ const mapping = {
3
+ Backquote: '`',
4
+ Minus: '-',
5
+ Equal: '=',
6
+ BracketLeft: '[',
7
+ BracketRight: ']',
8
+ Backslash: '\\',
9
+ NumpadDivide: '/',
10
+ NumpadMultiply: '*',
11
+ NumpadSubtract: '-',
12
+ Numpad7: 'N7',
13
+ Numpad8: 'N8',
14
+ Numpad9: 'N9',
15
+ NumpadAdd: '+',
16
+ Numpad4: 'N4',
17
+ Numpad5: 'N5',
18
+ Numpad6: 'N6',
19
+ Numpad1: 'N1',
20
+ Numpad2: 'N2',
21
+ Numpad3: 'N3',
22
+ NumpadEnter: 'Enter',
23
+ Numpad0: 'N0',
24
+ NumpadDecimal: '.',
25
+ IntlBackslash: '\\',
26
+ ArrowLeft: '←',
27
+ ArrowUp: '↑',
28
+ ArrowRight: '→',
29
+ ArrowDown: '↓',
30
+ Semicolon: ';',
31
+ Quote: '\'',
32
+ Comma: ',',
33
+ Period: '.',
34
+ Slash: '/',
35
+ mouseWheelUp: '▲',
36
+ mouseWheelDown: '▼'
37
+ }
38
+ if (code.startsWith('Key') && code.length === 4) {
39
+ return code[3].toLowerCase()
40
+ } else if (code.startsWith('Digit') && code.length === 5) {
41
+ return code[5]
42
+ } else {
43
+ return mapping[code] || code
44
+ }
45
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * session tabs component
3
+ * @param {array} props.tabs {id, title}
4
+ */
5
+
6
+ import React from 'react'
7
+ import { shortcutExtend } from './shortcut-handler.js'
8
+
9
+ class ShortcutControl extends React.PureComponent {
10
+ componentDidMount () {
11
+ window.addEventListener('keydown', this.handleKeyboardEvent.bind(this))
12
+ window.addEventListener('mousewheel', this.handleKeyboardEvent.bind(this))
13
+ }
14
+
15
+ prevTabShortcut = (e) => {
16
+ e.stopPropagation()
17
+ window.store.clickPrevTab()
18
+ }
19
+
20
+ nextTabShortcut = (e) => {
21
+ e.stopPropagation()
22
+ window.store.clickNextTab()
23
+ }
24
+
25
+ newBookmarkShortcut = (e) => {
26
+ e.stopPropagation()
27
+ window.store.onNewSsh()
28
+ }
29
+
30
+ togglefullscreenShortcut = (e) => {
31
+ e.stopPropagation()
32
+ document.querySelector('.term-fullscreen-control').click()
33
+ }
34
+
35
+ zoominShortcut = (e) => {
36
+ e.stopPropagation()
37
+ window.store.zoom(0.25, true)
38
+ }
39
+
40
+ zoomoutShortcut = (e) => {
41
+ e.stopPropagation()
42
+ window.store.zoom(-0.25, true)
43
+ }
44
+
45
+ zoominTerminalShortcut = (event) => {
46
+ if (window.store.inActiveTerminal) {
47
+ window.store.zoomTerminal(event.wheelDeltaY)
48
+ } else {
49
+ const plus = event.wheelDeltaY > 0 ? 0.2 : -0.2
50
+ window.store.zoom(plus, true)
51
+ }
52
+ }
53
+
54
+ zoomoutTerminalShortcut = (event) => {
55
+ this.zoominTerminalShortcut(event)
56
+ }
57
+
58
+ render () {
59
+ return null
60
+ }
61
+ }
62
+
63
+ export default shortcutExtend(ShortcutControl)
@@ -0,0 +1,194 @@
1
+ import { PureComponent } from 'react'
2
+ import {
3
+ Button,
4
+ Input,
5
+ message
6
+ } from 'antd'
7
+ import {
8
+ EditFilled,
9
+ CheckOutlined,
10
+ CloseOutlined
11
+ } from '@ant-design/icons'
12
+ import { debounce } from 'lodash-es'
13
+ import { getKeyCharacter } from './get-key-char.js'
14
+
15
+ export default class ShortcutEdit extends PureComponent {
16
+ state = {
17
+ editMode: false,
18
+ shortcut: '',
19
+ data: null
20
+ }
21
+
22
+ addEventListener = () => {
23
+ const elem = document.querySelector('.ant-drawer')
24
+ elem?.addEventListener('click', this.handleClickOuter)
25
+ document.addEventListener('keydown', this.handleKeyDown)
26
+ document.addEventListener('mousewheel', this.handleKeyDown)
27
+ }
28
+
29
+ removeEventListener = () => {
30
+ const elem = document.querySelector('.ant-drawer')
31
+ elem?.removeEventListener('click', this.handleClickOuter)
32
+ document.removeEventListener('keydown', this.handleKeyDown)
33
+ document.removeEventListener('mousewheel', this.handleKeyDown)
34
+ }
35
+
36
+ isInsideElement = (event) => {
37
+ const { target } = event
38
+ const cls = this.getCls()
39
+ if (!target || !target.classList) {
40
+ return false
41
+ } else if (target.classList.contains(cls)) {
42
+ return true
43
+ } else {
44
+ const parent = target.parentElement
45
+ if (parent !== null) {
46
+ return this.isInsideElement({ target: parent })
47
+ } else {
48
+ return false
49
+ }
50
+ }
51
+ }
52
+
53
+ handleClickOuter = (e) => {
54
+ if (!this.isInsideElement(e)) {
55
+ this.handleCancel()
56
+ }
57
+ }
58
+
59
+ handleEditClick = () => {
60
+ this.setState({
61
+ editMode: true
62
+ }, this.addEventListener)
63
+ }
64
+
65
+ handleCancel = () => {
66
+ this.setState({
67
+ editMode: false
68
+ }, this.removeEventListener)
69
+ }
70
+
71
+ handleConfirm = () => {
72
+ const {
73
+ name
74
+ } = this.props.data
75
+ this.props.updateConfig(
76
+ name, this.state.shortcut
77
+ )
78
+ this.handleCancel()
79
+ }
80
+
81
+ getCls = () => {
82
+ const { index } = this.props.data
83
+ return 'shortcut-control-' + index
84
+ }
85
+
86
+ warnCtrolKey = debounce(() => {
87
+ message.info(
88
+ 'Must have one of Ctrl or Shift or Alt or Meta key',
89
+ undefined
90
+ )
91
+ }, 3000)
92
+
93
+ warnExist = debounce(() => {
94
+ message.info(
95
+ 'Shortcut already exists',
96
+ undefined
97
+ )
98
+ }, 3000)
99
+
100
+ handleKeyDown = (e) => {
101
+ const {
102
+ code,
103
+ ctrlKey,
104
+ shiftKey,
105
+ metaKey,
106
+ altKey,
107
+ wheelDeltaY
108
+ } = e
109
+ const codeName = e instanceof window.WheelEvent
110
+ ? (wheelDeltaY > 0 ? 'mouseWheelUp' : 'mouseWheelDown')
111
+ : code
112
+ const codeK = getKeyCharacter(codeName)
113
+ const noControlKey = !ctrlKey && !shiftKey && !metaKey && !altKey
114
+ if (noControlKey && codeK === 'Escape') {
115
+ return this.handleCancel()
116
+ } else if (noControlKey) {
117
+ return this.warnCtrolKey()
118
+ }
119
+ const r = (ctrlKey ? 'ctrl+' : '') +
120
+ (metaKey ? 'meta+' : '') +
121
+ (shiftKey ? 'shift+' : '') +
122
+ (altKey ? 'alt+' : '') +
123
+ codeK.toLowerCase()
124
+ if (this.props.keysTaken[r]) {
125
+ return this.warnExist()
126
+ }
127
+ this.setState({
128
+ shortcut: r
129
+ })
130
+ }
131
+
132
+ handleClickOutside = () => {
133
+ this.removeEventListener()
134
+ this.setState({
135
+ editMode: false
136
+ })
137
+ }
138
+
139
+ renderStatic () {
140
+ const {
141
+ shortcut
142
+ } = this.props.data
143
+ return (
144
+ <Button
145
+ className='edit-shortcut-button'
146
+ onClick={this.handleEditClick}
147
+ >
148
+ <span>{shortcut}</span>
149
+ <EditFilled
150
+ className='shortcut-edit-icon pointer mg1l'
151
+ />
152
+ </Button>
153
+ )
154
+ }
155
+
156
+ renderAfter () {
157
+ const {
158
+ shortcut
159
+ } = this.state
160
+ if (!shortcut) {
161
+ return null
162
+ }
163
+ return (
164
+ <div>
165
+ <CheckOutlined
166
+ onClick={this.handleConfirm}
167
+ className='pointer'
168
+ />
169
+ <CloseOutlined
170
+ onClick={this.handleCancel}
171
+ className='pointer mg1l'
172
+ />
173
+ </div>
174
+ )
175
+ }
176
+
177
+ render () {
178
+ const {
179
+ shortcut,
180
+ editMode
181
+ } = this.state
182
+ if (!editMode) {
183
+ return this.renderStatic()
184
+ }
185
+ return (
186
+ <div className={this.getCls()}>
187
+ <Input
188
+ addonAfter={this.renderAfter()}
189
+ value={shortcut}
190
+ />
191
+ </div>
192
+ )
193
+ }
194
+ }
@@ -0,0 +1,76 @@
1
+ import { getKeyCharacter } from './get-key-char.js'
2
+ import shortcutsDefaultsGen from './shortcuts-defaults.js'
3
+ import {
4
+ isMacJs
5
+ } from '../../common/constants'
6
+
7
+ function buildConfig (config) {
8
+ const defs = shortcutsDefaultsGen()
9
+ const { shortcuts = {} } = config
10
+ return defs.reduce((p, c) => {
11
+ const propName = isMacJs ? 'shortcutMac' : 'shortcut'
12
+ const name = c.name + '_' + propName
13
+ const [type, func] = c.name.split('_')
14
+ return {
15
+ ...p,
16
+ [name]: {
17
+ shortcut: c.readonly ? c[propName] : (shortcuts[name] || c[propName]),
18
+ type,
19
+ func
20
+ }
21
+ }
22
+ }, {})
23
+ }
24
+
25
+ export function shortcutExtend (Cls) {
26
+ Cls.prototype.handleKeyboardEvent = function (event) {
27
+ // console.log('event', event)
28
+ const {
29
+ code,
30
+ ctrlKey,
31
+ shiftKey,
32
+ metaKey,
33
+ altKey,
34
+ wheelDeltaY
35
+ } = event
36
+ const codeName = event instanceof window.WheelEvent
37
+ ? (wheelDeltaY > 0 ? 'mouseWheelUp' : 'mouseWheelDown')
38
+ : code
39
+ const codeK = getKeyCharacter(codeName)
40
+ const noControlKey = !ctrlKey && !shiftKey && !metaKey && !altKey
41
+ if (noControlKey) {
42
+ return
43
+ }
44
+ const r = (ctrlKey ? 'ctrl+' : '') +
45
+ (metaKey ? 'meta+' : '') +
46
+ (shiftKey ? 'shift+' : '') +
47
+ (altKey ? 'alt+' : '') +
48
+ codeK.toLowerCase()
49
+ const shortcutsConfig = buildConfig(this.props.config)
50
+ const keys = Object.keys(shortcutsConfig)
51
+ const len = keys.length
52
+ for (let i = 0; i < len; i++) {
53
+ const k = keys[i]
54
+ const conf = shortcutsConfig[k]
55
+ const funcName = conf.func + 'Shortcut'
56
+ if (conf.shortcut.split(',').includes(r)) {
57
+ if (this[funcName]) {
58
+ this[funcName](event)
59
+ } else {
60
+ return false
61
+ }
62
+ }
63
+ }
64
+ }
65
+ return Cls
66
+ }
67
+
68
+ export function shortcutDescExtend (Cls) {
69
+ Cls.prototype.getShortcut = function (name) {
70
+ const shortcutsConfig = buildConfig(this.props.config)
71
+ const propName = isMacJs ? 'shortcutMac' : 'shortcut'
72
+ const n = `${name}_${propName}`
73
+ return shortcutsConfig[n].shortcut
74
+ }
75
+ return Cls
76
+ }
File without changes