@electerm/electerm-react 1.37.1 → 1.37.16

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.
@@ -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,
@@ -39,5 +39,6 @@ export default {
39
39
  initDefaultTabOnStart: true,
40
40
  screenReaderMode: false,
41
41
  autoRefreshWhenSwitchToSftp: false,
42
- hideSshConfig: false
42
+ hideSshConfig: false,
43
+ addTimeStampToTermLog: false
43
44
  }
@@ -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(':') && basePath === '/'
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 noStyle required name={[field.name, 'keyword']}>
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 ? osResolve(appPath, 'electerm', 'session_logs') : window.et.sessionLogPath
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={f('supportRegexp')}
464
+ title={tip}
455
465
  />
456
466
  </div>
457
467
  <KeywordForm
@@ -475,6 +485,9 @@ export default class SettingTerminal extends Component {
475
485
  {
476
486
  this.renderCursorStyleSelect()
477
487
  }
488
+ {this.renderToggle('saveTerminalLogToFile', (
489
+ <ShowItem to={terminalLogPath} className='mg1l'>{p('open')}</ShowItem>
490
+ ))}
478
491
  {
479
492
  [
480
493
  'cursorBlink',
@@ -484,9 +497,6 @@ export default class SettingTerminal extends Component {
484
497
  'ctrlOrMetaOpenTerminalLink'
485
498
  ].map(this.renderToggle)
486
499
  }
487
- {this.renderToggle('saveTerminalLogToFile', (
488
- <ShowItem to={terminalLogPath} className='mg1l'>{p('open')}</ShowItem>
489
- ))}
490
500
  {this.renderReset()}
491
501
  </div>
492
502
  )
@@ -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.gz bin
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.gz`)
19
- const cmd = `tar -C ${path} -czf ${np} ${name}`
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 -xzf "${from}" -C "${to}"`
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 = throttle((e) => {
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
- }, 300)
35
+ }
37
36
 
38
- splitShortcut = throttle((e) => {
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
- }, 300)
41
+ }
43
42
 
44
43
  zoominShortcut = (e) => {
45
44
  e.stopPropagation()
@@ -3,9 +3,10 @@ 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
- function buildConfig (config) {
8
- const defs = shortcutsDefaultsGen()
8
+ function buildConfig (config, filter = d => d) {
9
+ const defs = shortcutsDefaultsGen().filter(filter)
9
10
  const { shortcuts = {} } = config
10
11
  return defs.reduce((p, c) => {
11
12
  const propName = isMacJs ? 'shortcutMac' : 'shortcut'
@@ -23,7 +24,7 @@ function buildConfig (config) {
23
24
  }
24
25
 
25
26
  export function shortcutExtend (Cls) {
26
- Cls.prototype.handleKeyboardEvent = function (event) {
27
+ Cls.prototype.handleKeyboardEvent = throttle(function (event) {
27
28
  const {
28
29
  code,
29
30
  ctrlKey,
@@ -45,7 +46,7 @@ export function shortcutExtend (Cls) {
45
46
  (shiftKey ? 'shift+' : '') +
46
47
  (altKey ? 'alt+' : '') +
47
48
  codeK.toLowerCase()
48
- const shortcutsConfig = buildConfig(this.props.config)
49
+ const shortcutsConfig = buildConfig(this.props.config, d => !d.readonly)
49
50
  const keys = Object.keys(shortcutsConfig)
50
51
  const len = keys.length
51
52
  for (let i = 0; i < len; i++) {
@@ -60,7 +61,7 @@ export function shortcutExtend (Cls) {
60
61
  }
61
62
  }
62
63
  }
63
- }
64
+ }, 300)
64
65
  return Cls
65
66
  }
66
67
 
@@ -36,16 +36,18 @@ export default class Shortcuts extends Component {
36
36
 
37
37
  getData () {
38
38
  const { shortcuts = {} } = this.props.store.config
39
- return shortcutsDefaults.map((c, i) => {
40
- const propName = isMac ? 'shortcutMac' : 'shortcut'
41
- const name = c.name + '_' + propName
42
- return {
43
- index: i + 1,
44
- name,
45
- readonly: c.readonly,
46
- shortcut: c.readonly ? c[propName] : (shortcuts[name] || c[propName])
47
- }
48
- })
39
+ return shortcutsDefaults
40
+ .filter(g => !g.readonly)
41
+ .map((c, i) => {
42
+ const propName = isMac ? 'shortcutMac' : 'shortcut'
43
+ const name = c.name + '_' + propName
44
+ return {
45
+ index: i + 1,
46
+ name,
47
+ readonly: c.readonly,
48
+ shortcut: c.readonly ? c[propName] : (shortcuts[name] || c[propName])
49
+ }
50
+ })
49
51
  }
50
52
 
51
53
  getKeysTakenData = () => {
@@ -35,8 +35,12 @@ export class KeywordHighlighterAddon {
35
35
  color = 'red'
36
36
  } = obj || {}
37
37
  if (keyword) {
38
- const regex = new RegExp(`(${keyword})`, 'gi')
39
- text = text.replace(regex, this.colorize(color))
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'
@@ -78,6 +79,7 @@ class Term extends Component {
78
79
  promoteModalVisible: false,
79
80
  savePassword: false,
80
81
  saveTerminalLogToFile: !!this.props.config.saveTerminalLogToFile,
82
+ addTimeStampToTermLog: !!this.props.config.addTimeStampToTermLog,
81
83
  tempPassword: '',
82
84
  passType: 'password',
83
85
  zmodemTransfer: null,
@@ -232,15 +234,15 @@ class Term extends Component {
232
234
  this.term.selectAll()
233
235
  }
234
236
 
235
- copyShortcut = (e) => {
236
- const sel = this.term.getSelection()
237
- if (sel) {
238
- e.stopPropagation()
239
- e.preventDefault()
240
- this.copySelectionToClipboard()
241
- return false
242
- }
243
- }
237
+ // copyShortcut = (e) => {
238
+ // const sel = this.term.getSelection()
239
+ // if (sel) {
240
+ // e.stopPropagation()
241
+ // e.preventDefault()
242
+ // this.copySelectionToClipboard()
243
+ // return false
244
+ // }
245
+ // }
244
246
 
245
247
  searchShortcut = (e) => {
246
248
  e.stopPropagation()
@@ -274,6 +276,7 @@ class Term extends Component {
274
276
  action,
275
277
  encode,
276
278
  saveTerminalLogToFile,
279
+ addTimeStampToTermLog,
277
280
  type,
278
281
  cmd,
279
282
  activeSplitId,
@@ -348,17 +351,10 @@ class Term extends Component {
348
351
  ) {
349
352
  postMessage({
350
353
  action: commonActions.returnTermLogState,
351
- state: this.state.saveTerminalLogToFile,
352
- pid: statePid
353
- })
354
- postMessage({
355
- action: commonActions.returnTermLogState,
356
- state: this.state.saveTerminalLogToFile,
357
- pid: statePid
358
- })
359
- postMessage({
360
- action: commonActions.returnTermLogState,
361
- state: this.state.saveTerminalLogToFile,
354
+ state: {
355
+ saveTerminalLogToFile: this.state.saveTerminalLogToFile,
356
+ addTimeStampToTermLog: this.state.addTimeStampToTermLog
357
+ },
362
358
  pid: statePid
363
359
  })
364
360
  } else if (
@@ -366,6 +362,7 @@ class Term extends Component {
366
362
  pid === statePid
367
363
  ) {
368
364
  this.setState({
365
+ addTimeStampToTermLog,
369
366
  saveTerminalLogToFile
370
367
  })
371
368
  }
@@ -674,14 +671,6 @@ class Term extends Component {
674
671
  window.store.toggleTerminalSearch()
675
672
  }
676
673
 
677
- // onLineFeed = e => {
678
- // // console.log(e, 'onLineFeed')
679
- // }
680
-
681
- onTitleChange = e => {
682
- log.debug(e, 'title change')
683
- }
684
-
685
674
  onSearchResultsChange = ({ resultIndex, resultCount }) => {
686
675
  window.store.storeAssign({
687
676
  termSearchMatchCount: resultCount,
@@ -779,12 +768,32 @@ class Term extends Component {
779
768
  }
780
769
  }
781
770
 
771
+ parse (rawText) {
772
+ let result = ''
773
+ const len = rawText.length
774
+ for (let i = 0; i < len; i++) {
775
+ if (rawText[i] === '\b') {
776
+ result = result.slice(0, -1)
777
+ } else {
778
+ result += rawText[i]
779
+ }
780
+ }
781
+ return result
782
+ }
783
+
784
+ onKey = ({ key }) => {
785
+ if (key === '\x7F') {
786
+ this.dataCache = this.dataCache.slice(0, -1)
787
+ } else {
788
+ this.dataCache += key
789
+ }
790
+ }
791
+
782
792
  onData = (d) => {
783
- if (!d.includes('\r') && !d.includes('\r')) {
784
- this.dataCache += d
793
+ if (!d.includes('\r')) {
785
794
  delete this.userTypeExit
786
795
  } else {
787
- const data = this.dataCache.trim()
796
+ const data = this.parse(this.dataCache.trim())
788
797
  this.dataCache = ''
789
798
  if (data === 'exit') {
790
799
  this.userTypeExit = true
@@ -827,27 +836,31 @@ class Term extends Component {
827
836
  fontSize: tab.fontSize || config.fontSize,
828
837
  screenReaderMode: config.screenReaderMode
829
838
  })
839
+
840
+ // term.onLineFeed(this.onLineFeed)
841
+ // term.onTitleChange(this.onTitleChange)
842
+ term.onSelectionChange(this.onSelection)
843
+ this.loadState(term)
844
+ term.open(document.getElementById(id), true)
845
+ this.loadRenderer(term, config)
846
+ term.textarea.addEventListener('focus', this.setActive)
847
+ term.onKey(this.onKey)
848
+ // term.textarea.addEventListener('blur', this.onBlur)
849
+
850
+ // term.on('keydown', this.handleEvent)
830
851
  this.fitAddon = new FitAddon()
831
852
  this.searchAddon = new SearchAddon()
853
+ const ligtureAddon = new LigaturesAddon()
832
854
  this.searchAddon.onDidChangeResults(this.onSearchResultsChange)
833
855
  const unicode11Addon = new Unicode11Addon()
834
856
  this.serializeAddon = new SerializeAddon()
835
857
  term.loadAddon(this.serializeAddon)
836
858
  term.loadAddon(unicode11Addon)
859
+ term.loadAddon(ligtureAddon)
837
860
  // activate the new version
838
861
  term.unicode.activeVersion = '11'
839
862
  term.loadAddon(this.fitAddon)
840
863
  term.loadAddon(this.searchAddon)
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.textarea.addEventListener('blur', this.onBlur)
849
-
850
- // term.on('keydown', this.handleEvent)
851
864
  term.onData(this.onData)
852
865
  this.term = term
853
866
  term.attachCustomKeyEventHandler(this.handleKeyboardEvent.bind(this))
@@ -963,6 +976,7 @@ class Term extends Component {
963
976
  ...extra,
964
977
  logName,
965
978
  ...pick(config, [
979
+ 'addTimeStampToTermLog',
966
980
  'keepaliveInterval',
967
981
  'keepaliveCountMax',
968
982
  'execWindows',
@@ -37,3 +37,11 @@ export function toggleTerminalLog (pid, sessionId) {
37
37
  action: 'toggle-terminal-log'
38
38
  })
39
39
  }
40
+
41
+ export function toggleTerminalLogTimestamp (pid, sessionId) {
42
+ return fetch({
43
+ pid,
44
+ sessionId,
45
+ action: 'toggle-terminal-log-timestamp'
46
+ })
47
+ }
@@ -11,14 +11,15 @@ import {
11
11
  } from 'antd'
12
12
  import ShowItem from '../common/show-item'
13
13
  import postMsg from '../../common/post-msg'
14
- import { toggleTerminalLog } from '../terminal/terminal-apis'
14
+ import { toggleTerminalLog, toggleTerminalLogTimestamp } from '../terminal/terminal-apis'
15
15
 
16
16
  const { prefix } = window
17
17
  const st = prefix('setting')
18
18
 
19
19
  export default class TerminalInfoBase extends Component {
20
20
  state = {
21
- saveTerminalLogToFile: false
21
+ saveTerminalLogToFile: false,
22
+ addTimeStampToTermLog: false
22
23
  }
23
24
 
24
25
  componentDidMount () {
@@ -30,8 +31,31 @@ export default class TerminalInfoBase extends Component {
30
31
  this.exit()
31
32
  }
32
33
 
34
+ handleToggleTimestamp = () => {
35
+ const { saveTerminalLogToFile, addTimeStampToTermLog } = this.state
36
+ const {
37
+ pid,
38
+ sessionId
39
+ } = this.props
40
+ toggleTerminalLogTimestamp(
41
+ pid,
42
+ sessionId
43
+ )
44
+ const nv = !addTimeStampToTermLog
45
+ this.setState({
46
+ addTimeStampToTermLog: nv
47
+ })
48
+ postMsg({
49
+ action: commonActions.setTermLogState,
50
+ pid,
51
+ addTimeStampToTermLog: nv,
52
+ saveTerminalLogToFile,
53
+ sessionId
54
+ })
55
+ }
56
+
33
57
  handleToggle = () => {
34
- const { saveTerminalLogToFile } = this.state
58
+ const { saveTerminalLogToFile, addTimeStampToTermLog } = this.state
35
59
  const {
36
60
  pid,
37
61
  sessionId
@@ -48,6 +72,7 @@ export default class TerminalInfoBase extends Component {
48
72
  action: commonActions.setTermLogState,
49
73
  pid,
50
74
  saveTerminalLogToFile: nv,
75
+ addTimeStampToTermLog,
51
76
  sessionId
52
77
  })
53
78
  }
@@ -62,9 +87,7 @@ export default class TerminalInfoBase extends Component {
62
87
  action === commonActions.returnTermLogState &&
63
88
  this.props.pid === ppid
64
89
  ) {
65
- this.setState({
66
- saveTerminalLogToFile: state
67
- })
90
+ this.setState(state)
68
91
  window.removeEventListener('message', this.onEvent)
69
92
  }
70
93
  }
@@ -85,6 +108,22 @@ export default class TerminalInfoBase extends Component {
85
108
  window.removeEventListener('message', this.onEvent)
86
109
  }
87
110
 
111
+ renderTimestamp () {
112
+ const { saveTerminalLogToFile, addTimeStampToTermLog } = this.state
113
+ if (!saveTerminalLogToFile) {
114
+ return null
115
+ }
116
+ const name = st('addTimeStampToTermLog')
117
+ return (
118
+ <Switch
119
+ checkedChildren={name}
120
+ unCheckedChildren={name}
121
+ checked={addTimeStampToTermLog}
122
+ onChange={this.handleToggleTimestamp}
123
+ />
124
+ )
125
+ }
126
+
88
127
  render () {
89
128
  const {
90
129
  id,
@@ -110,7 +149,11 @@ export default class TerminalInfoBase extends Component {
110
149
  unCheckedChildren={name}
111
150
  checked={saveTerminalLogToFile}
112
151
  onChange={this.handleToggle}
152
+ className='mg1r'
113
153
  />
154
+ {
155
+ this.renderTimestamp()
156
+ }
114
157
  </span>
115
158
  </div>
116
159
  <p><b>log:</b> {to}</p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "1.37.1",
3
+ "version": "1.37.16",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",