@electerm/electerm-react 1.37.6 → 1.37.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 +1 -0
- package/client/common/resolve.js +2 -2
- package/client/components/setting-panel/keywords-form.jsx +16 -1
- package/client/components/setting-panel/setting-terminal.jsx +13 -4
- package/client/components/sftp/zip.js +4 -4
- package/client/components/shortcuts/shortcut-control.jsx +6 -7
- package/client/components/shortcuts/shortcut-handler.js +29 -7
- package/client/components/shortcuts/shortcuts-defaults.js +3 -0
- package/client/components/tabs/tab-title.jsx +1 -1
- package/client/components/terminal/highlight-addon.js +6 -2
- package/client/components/terminal/index.jsx +48 -22
- package/package.json +1 -1
|
@@ -307,6 +307,7 @@ export const localAddrBookmarkLsKey = 'local-addr-bookmark-keys'
|
|
|
307
307
|
export const sshTunnelHelpLink = 'https://github.com/electerm/electerm/wiki/How-to-use-ssh-tunnel'
|
|
308
308
|
export const batchOpHelpLink = 'https://github.com/electerm/electerm/wiki/batch-operation'
|
|
309
309
|
export const proxyHelpLink = 'https://github.com/electerm/electerm/wiki/proxy-format'
|
|
310
|
+
export const regexHelpLink = 'https://github.com/electerm/electerm/wiki/Terminal-keywords-highlight-regular-expression-exmaples'
|
|
310
311
|
export const modals = {
|
|
311
312
|
hide: 0,
|
|
312
313
|
setting: 1,
|
package/client/common/resolve.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
export default (basePath, nameOrDot) => {
|
|
9
|
-
const sep = basePath.includes('\\') || basePath.includes('
|
|
9
|
+
const sep = basePath.includes('\\') || basePath.includes(':\\')
|
|
10
10
|
? '\\'
|
|
11
11
|
: '/'
|
|
12
12
|
if (nameOrDot === '..') {
|
|
@@ -18,7 +18,7 @@ export default (basePath, nameOrDot) => {
|
|
|
18
18
|
const res = arr.slice(0, length - 1).join(sep)
|
|
19
19
|
return res || '/'
|
|
20
20
|
}
|
|
21
|
-
const pre = nameOrDot.includes('
|
|
21
|
+
const pre = nameOrDot.includes(':\\') && basePath === '/'
|
|
22
22
|
? ''
|
|
23
23
|
: basePath
|
|
24
24
|
return pre +
|
|
@@ -30,16 +30,31 @@ export default function KeywordForm (props) {
|
|
|
30
30
|
props.submit(data)
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
function checker (_, value) {
|
|
34
|
+
try {
|
|
35
|
+
return Promise.resolve(!!new RegExp(`(${value})`, 'gi'))
|
|
36
|
+
} catch (e) {
|
|
37
|
+
console.log(e)
|
|
38
|
+
return Promise.reject(e)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
33
42
|
function renderItem (field, i, add, remove) {
|
|
34
43
|
return (
|
|
35
44
|
<Space
|
|
36
45
|
align='center'
|
|
37
46
|
key={field.key}
|
|
47
|
+
className='mg3r'
|
|
38
48
|
>
|
|
39
49
|
<FormItem
|
|
40
50
|
hasFeedback
|
|
41
51
|
>
|
|
42
|
-
<FormItem
|
|
52
|
+
<FormItem
|
|
53
|
+
noStyle
|
|
54
|
+
required
|
|
55
|
+
name={[field.name, 'keyword']}
|
|
56
|
+
rules={[{ validator: checker }]}
|
|
57
|
+
>
|
|
43
58
|
<Input
|
|
44
59
|
addonBefore={renderBefore(field.name)}
|
|
45
60
|
/>
|
|
@@ -17,7 +17,8 @@ import {
|
|
|
17
17
|
import deepCopy from 'json-deep-copy'
|
|
18
18
|
import {
|
|
19
19
|
noTerminalBgValue,
|
|
20
|
-
rendererTypes
|
|
20
|
+
rendererTypes,
|
|
21
|
+
regexHelpLink
|
|
21
22
|
} from '../../common/constants'
|
|
22
23
|
import defaultSettings from '../../common/default-setting'
|
|
23
24
|
import ShowItem from '../common/show-item'
|
|
@@ -25,6 +26,7 @@ import { osResolve } from '../../common/resolve'
|
|
|
25
26
|
import { isNumber, isNaN } from 'lodash-es'
|
|
26
27
|
import mapper from '../../common/auto-complete-data-mapper'
|
|
27
28
|
import KeywordForm from './keywords-form'
|
|
29
|
+
import Link from '../common/external-link'
|
|
28
30
|
import HelpIcon from '../common/help-icon'
|
|
29
31
|
import './setting.styl'
|
|
30
32
|
|
|
@@ -406,7 +408,15 @@ export default class SettingTerminal extends Component {
|
|
|
406
408
|
},
|
|
407
409
|
submit: this.handleSubmitKeywords
|
|
408
410
|
}
|
|
409
|
-
const terminalLogPath = appPath
|
|
411
|
+
const terminalLogPath = appPath
|
|
412
|
+
? osResolve(appPath, 'electerm', 'session_logs')
|
|
413
|
+
: window.et.sessionLogPath
|
|
414
|
+
const tip = (
|
|
415
|
+
<div>
|
|
416
|
+
<span className='mg1r'>{f('supportRegexp')}</span>
|
|
417
|
+
<Link to={regexHelpLink}>wiki</Link>
|
|
418
|
+
</div>
|
|
419
|
+
)
|
|
410
420
|
return (
|
|
411
421
|
<div className='form-wrap pd1y pd2x'>
|
|
412
422
|
<div className='pd1y font16 bold'>
|
|
@@ -451,7 +461,7 @@ export default class SettingTerminal extends Component {
|
|
|
451
461
|
<div className='pd1b'>
|
|
452
462
|
<span className='inline-title mg1r'>{f('keywordsHighlight')}</span>
|
|
453
463
|
<HelpIcon
|
|
454
|
-
title={
|
|
464
|
+
title={tip}
|
|
455
465
|
/>
|
|
456
466
|
</div>
|
|
457
467
|
<KeywordForm
|
|
@@ -480,7 +490,6 @@ export default class SettingTerminal extends Component {
|
|
|
480
490
|
))}
|
|
481
491
|
{
|
|
482
492
|
[
|
|
483
|
-
'addTimeStampToTermLog',
|
|
484
493
|
'cursorBlink',
|
|
485
494
|
'rightClickSelectsWord',
|
|
486
495
|
'pasteWhenContextMenu',
|
|
@@ -12,17 +12,17 @@ const isRemote = true
|
|
|
12
12
|
const temp = '/tmp'
|
|
13
13
|
|
|
14
14
|
export async function zipCmd (pid, sessionId, filePath) {
|
|
15
|
-
// tar -czf bin.tar
|
|
15
|
+
// tar -czf bin.tar bin
|
|
16
16
|
const id = generate()
|
|
17
17
|
const { path, name } = getFolderFromFilePath(filePath, isRemote)
|
|
18
|
-
const np = resolve(temp, `electerm-${id}.tar
|
|
19
|
-
const cmd = `tar -C ${path} -
|
|
18
|
+
const np = resolve(temp, `electerm-${id}.tar`)
|
|
19
|
+
const cmd = `tar -C ${path} -cf ${np} ${name}`
|
|
20
20
|
await runCmd(pid, sessionId, cmd)
|
|
21
21
|
return np
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export function unzipCmd (pid, sessionId, from, to) {
|
|
25
|
-
const cmd = `tar -
|
|
25
|
+
const cmd = `tar -xf "${from}" -C "${to}"`
|
|
26
26
|
return runCmd(pid, sessionId, cmd)
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
import React from 'react'
|
|
7
7
|
import { shortcutExtend } from './shortcut-handler.js'
|
|
8
|
-
import { throttle } from 'lodash-es'
|
|
9
8
|
|
|
10
9
|
class ShortcutControl extends React.PureComponent {
|
|
11
10
|
componentDidMount () {
|
|
@@ -28,18 +27,18 @@ class ShortcutControl extends React.PureComponent {
|
|
|
28
27
|
window.store.onNewSsh()
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
togglefullscreenShortcut =
|
|
30
|
+
togglefullscreenShortcut = (e) => {
|
|
32
31
|
e.stopPropagation()
|
|
33
32
|
const x = document.querySelector('.term-fullscreen-control') ||
|
|
34
|
-
document.querySelector('.term-fullscreen-control1')
|
|
33
|
+
document.querySelector('.session-current .term-fullscreen-control1')
|
|
35
34
|
x && x.click()
|
|
36
|
-
}
|
|
35
|
+
}
|
|
37
36
|
|
|
38
|
-
splitShortcut =
|
|
37
|
+
splitShortcut = (e) => {
|
|
39
38
|
e.stopPropagation()
|
|
40
|
-
const x = document.querySelector('.icon-split')
|
|
39
|
+
const x = document.querySelector('.session-current .icon-split')
|
|
41
40
|
x && x.click()
|
|
42
|
-
}
|
|
41
|
+
}
|
|
43
42
|
|
|
44
43
|
zoominShortcut = (e) => {
|
|
45
44
|
e.stopPropagation()
|
|
@@ -3,10 +3,32 @@ import shortcutsDefaultsGen from './shortcuts-defaults.js'
|
|
|
3
3
|
import {
|
|
4
4
|
isMacJs
|
|
5
5
|
} from '../../common/constants'
|
|
6
|
+
import { throttle } from 'lodash-es'
|
|
6
7
|
|
|
7
8
|
function buildConfig (config, filter = d => d) {
|
|
8
9
|
const defs = shortcutsDefaultsGen().filter(filter)
|
|
9
10
|
const { shortcuts = {} } = config
|
|
11
|
+
return defs.reduce((p, c) => {
|
|
12
|
+
const propName = isMacJs ? 'shortcutMac' : 'shortcut'
|
|
13
|
+
if (isMacJs && c.skipMac) {
|
|
14
|
+
return p
|
|
15
|
+
}
|
|
16
|
+
const name = c.name + '_' + propName
|
|
17
|
+
const [type, func] = c.name.split('_')
|
|
18
|
+
return {
|
|
19
|
+
...p,
|
|
20
|
+
[name]: {
|
|
21
|
+
shortcut: c.readonly ? c[propName] : (shortcuts[name] || c[propName]),
|
|
22
|
+
type,
|
|
23
|
+
func
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}, {})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function buildConfigForSearch (config) {
|
|
30
|
+
const defs = shortcutsDefaultsGen()
|
|
31
|
+
const { shortcuts = {} } = config
|
|
10
32
|
return defs.reduce((p, c) => {
|
|
11
33
|
const propName = isMacJs ? 'shortcutMac' : 'shortcut'
|
|
12
34
|
const name = c.name + '_' + propName
|
|
@@ -23,7 +45,7 @@ function buildConfig (config, filter = d => d) {
|
|
|
23
45
|
}
|
|
24
46
|
|
|
25
47
|
export function shortcutExtend (Cls) {
|
|
26
|
-
Cls.prototype.handleKeyboardEvent = function (event) {
|
|
48
|
+
Cls.prototype.handleKeyboardEvent = throttle(function (event) {
|
|
27
49
|
const {
|
|
28
50
|
code,
|
|
29
51
|
ctrlKey,
|
|
@@ -45,7 +67,7 @@ export function shortcutExtend (Cls) {
|
|
|
45
67
|
(shiftKey ? 'shift+' : '') +
|
|
46
68
|
(altKey ? 'alt+' : '') +
|
|
47
69
|
codeK.toLowerCase()
|
|
48
|
-
const shortcutsConfig = buildConfig(this.props.config, d => !d.
|
|
70
|
+
const shortcutsConfig = buildConfig(this.props.config, d => !d.hidden)
|
|
49
71
|
const keys = Object.keys(shortcutsConfig)
|
|
50
72
|
const len = keys.length
|
|
51
73
|
for (let i = 0; i < len; i++) {
|
|
@@ -54,19 +76,19 @@ export function shortcutExtend (Cls) {
|
|
|
54
76
|
const funcName = conf.func + 'Shortcut'
|
|
55
77
|
if (conf.shortcut.split(',').includes(r)) {
|
|
56
78
|
if (this[funcName]) {
|
|
57
|
-
this[funcName](event)
|
|
58
|
-
} else {
|
|
59
|
-
return
|
|
79
|
+
return this[funcName](event)
|
|
80
|
+
} else if (this.isTerm) {
|
|
81
|
+
return true
|
|
60
82
|
}
|
|
61
83
|
}
|
|
62
84
|
}
|
|
63
|
-
}
|
|
85
|
+
}, 300)
|
|
64
86
|
return Cls
|
|
65
87
|
}
|
|
66
88
|
|
|
67
89
|
export function shortcutDescExtend (Cls) {
|
|
68
90
|
Cls.prototype.getShortcut = function (name) {
|
|
69
|
-
const shortcutsConfig =
|
|
91
|
+
const shortcutsConfig = buildConfigForSearch(this.props.config)
|
|
70
92
|
const propName = isMacJs ? 'shortcutMac' : 'shortcut'
|
|
71
93
|
const n = `${name}_${propName}`
|
|
72
94
|
return shortcutsConfig[n].shortcut
|
|
@@ -49,18 +49,21 @@ export default () => {
|
|
|
49
49
|
name: 'terminal_selectAll',
|
|
50
50
|
shortcut: 'ctrl+a,ctrl+shift+a',
|
|
51
51
|
shortcutMac: 'meta+a',
|
|
52
|
+
skipMac: true,
|
|
52
53
|
readonly: true
|
|
53
54
|
},
|
|
54
55
|
{
|
|
55
56
|
name: 'terminal_copy',
|
|
56
57
|
shortcut: 'ctrl+c,ctrl+shift+c',
|
|
57
58
|
shortcutMac: 'meta+c',
|
|
59
|
+
skipMac: true,
|
|
58
60
|
readonly: true
|
|
59
61
|
},
|
|
60
62
|
{
|
|
61
63
|
name: 'terminal_paste',
|
|
62
64
|
shortcut: 'ctrl+v,ctrl+shift+v',
|
|
63
65
|
shortcutMac: 'meta+v',
|
|
66
|
+
hidden: true,
|
|
64
67
|
readonly: true
|
|
65
68
|
},
|
|
66
69
|
{
|
|
@@ -35,8 +35,12 @@ export class KeywordHighlighterAddon {
|
|
|
35
35
|
color = 'red'
|
|
36
36
|
} = obj || {}
|
|
37
37
|
if (keyword) {
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
try {
|
|
39
|
+
const regex = new RegExp(`(${keyword})`, 'gi')
|
|
40
|
+
text = text.replace(regex, this.colorize(color))
|
|
41
|
+
} catch (e) {
|
|
42
|
+
window.store.onError(e)
|
|
43
|
+
}
|
|
40
44
|
}
|
|
41
45
|
}
|
|
42
46
|
return text
|
|
@@ -41,6 +41,7 @@ import { WebLinksAddon } from 'xterm-addon-web-links'
|
|
|
41
41
|
import { SerializeAddon } from 'xterm-addon-serialize'
|
|
42
42
|
import { CanvasAddon } from 'xterm-addon-canvas'
|
|
43
43
|
import { WebglAddon } from 'xterm-addon-webgl'
|
|
44
|
+
import { LigaturesAddon } from 'xterm-addon-ligatures'
|
|
44
45
|
import getProxy from '../../common/get-proxy'
|
|
45
46
|
import { Zmodem, AddonZmodem } from './xterm-zmodem'
|
|
46
47
|
import { Unicode11Addon } from 'xterm-addon-unicode11'
|
|
@@ -86,6 +87,8 @@ class Term extends Component {
|
|
|
86
87
|
}
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
isTerm = true
|
|
91
|
+
|
|
89
92
|
dataCache = ''
|
|
90
93
|
|
|
91
94
|
componentDidMount () {
|
|
@@ -233,15 +236,14 @@ class Term extends Component {
|
|
|
233
236
|
this.term.selectAll()
|
|
234
237
|
}
|
|
235
238
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
// }
|
|
239
|
+
copyShortcut = (e) => {
|
|
240
|
+
const sel = this.term.getSelection()
|
|
241
|
+
if (sel) {
|
|
242
|
+
e.stopPropagation()
|
|
243
|
+
this.copySelectionToClipboard()
|
|
244
|
+
return false
|
|
245
|
+
}
|
|
246
|
+
}
|
|
245
247
|
|
|
246
248
|
searchShortcut = (e) => {
|
|
247
249
|
e.stopPropagation()
|
|
@@ -767,12 +769,32 @@ class Term extends Component {
|
|
|
767
769
|
}
|
|
768
770
|
}
|
|
769
771
|
|
|
772
|
+
parse (rawText) {
|
|
773
|
+
let result = ''
|
|
774
|
+
const len = rawText.length
|
|
775
|
+
for (let i = 0; i < len; i++) {
|
|
776
|
+
if (rawText[i] === '\b') {
|
|
777
|
+
result = result.slice(0, -1)
|
|
778
|
+
} else {
|
|
779
|
+
result += rawText[i]
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
return result
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
onKey = ({ key }) => {
|
|
786
|
+
if (key === '\x7F') {
|
|
787
|
+
this.dataCache = this.dataCache.slice(0, -1)
|
|
788
|
+
} else {
|
|
789
|
+
this.dataCache += key
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
770
793
|
onData = (d) => {
|
|
771
|
-
if (!d.includes('\r')
|
|
772
|
-
this.dataCache += d
|
|
794
|
+
if (!d.includes('\r')) {
|
|
773
795
|
delete this.userTypeExit
|
|
774
796
|
} else {
|
|
775
|
-
const data = this.dataCache.trim()
|
|
797
|
+
const data = this.parse(this.dataCache.trim())
|
|
776
798
|
this.dataCache = ''
|
|
777
799
|
if (data === 'exit') {
|
|
778
800
|
this.userTypeExit = true
|
|
@@ -815,27 +837,31 @@ class Term extends Component {
|
|
|
815
837
|
fontSize: tab.fontSize || config.fontSize,
|
|
816
838
|
screenReaderMode: config.screenReaderMode
|
|
817
839
|
})
|
|
840
|
+
|
|
841
|
+
// term.onLineFeed(this.onLineFeed)
|
|
842
|
+
// term.onTitleChange(this.onTitleChange)
|
|
843
|
+
term.onSelectionChange(this.onSelection)
|
|
844
|
+
this.loadState(term)
|
|
845
|
+
term.open(document.getElementById(id), true)
|
|
846
|
+
this.loadRenderer(term, config)
|
|
847
|
+
term.textarea.addEventListener('focus', this.setActive)
|
|
848
|
+
term.onKey(this.onKey)
|
|
849
|
+
// term.textarea.addEventListener('blur', this.onBlur)
|
|
850
|
+
|
|
851
|
+
// term.on('keydown', this.handleEvent)
|
|
818
852
|
this.fitAddon = new FitAddon()
|
|
819
853
|
this.searchAddon = new SearchAddon()
|
|
854
|
+
const ligtureAddon = new LigaturesAddon()
|
|
820
855
|
this.searchAddon.onDidChangeResults(this.onSearchResultsChange)
|
|
821
856
|
const unicode11Addon = new Unicode11Addon()
|
|
822
857
|
this.serializeAddon = new SerializeAddon()
|
|
823
858
|
term.loadAddon(this.serializeAddon)
|
|
824
859
|
term.loadAddon(unicode11Addon)
|
|
860
|
+
term.loadAddon(ligtureAddon)
|
|
825
861
|
// activate the new version
|
|
826
862
|
term.unicode.activeVersion = '11'
|
|
827
863
|
term.loadAddon(this.fitAddon)
|
|
828
864
|
term.loadAddon(this.searchAddon)
|
|
829
|
-
// term.onLineFeed(this.onLineFeed)
|
|
830
|
-
// term.onTitleChange(this.onTitleChange)
|
|
831
|
-
term.onSelectionChange(this.onSelection)
|
|
832
|
-
this.loadState(term)
|
|
833
|
-
term.open(document.getElementById(id), true)
|
|
834
|
-
this.loadRenderer(term, config)
|
|
835
|
-
term.textarea.addEventListener('focus', this.setActive)
|
|
836
|
-
// term.textarea.addEventListener('blur', this.onBlur)
|
|
837
|
-
|
|
838
|
-
// term.on('keydown', this.handleEvent)
|
|
839
865
|
term.onData(this.onData)
|
|
840
866
|
this.term = term
|
|
841
867
|
term.attachCustomKeyEventHandler(this.handleKeyboardEvent.bind(this))
|