@electerm/electerm-react 1.37.81 → 1.37.92

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.
@@ -42,7 +42,7 @@ export const maxHistory = 50
42
42
  export const maxTransport = 5
43
43
  export const maxSftpHistory = 20
44
44
  export const maxZoom = 8
45
- export const minZoom = 0.25
45
+ export const minZoom = 0.5
46
46
  export const extraTabWidth = 113
47
47
  // export const maxTabs = 20
48
48
 
@@ -336,3 +336,4 @@ export const instSftpKeys = [
336
336
  'readFile',
337
337
  'writeFile'
338
338
  ]
339
+ export const cwdId = '=__+__'
@@ -40,5 +40,7 @@ export default {
40
40
  screenReaderMode: false,
41
41
  autoRefreshWhenSwitchToSftp: false,
42
42
  hideSshConfig: false,
43
- addTimeStampToTermLog: false
43
+ addTimeStampToTermLog: false,
44
+ sftpPathFollowSsh: false,
45
+ keepaliveInterval: 0
44
46
  }
@@ -1,6 +1,5 @@
1
1
  import { Component } from '../common/react-subx'
2
2
  import {
3
- Tooltip,
4
3
  Select
5
4
  } from 'antd'
6
5
  import { InfoCircleOutlined } from '@ant-design/icons'
@@ -109,14 +108,10 @@ export default class SystemMenu extends Component {
109
108
  }
110
109
  return (
111
110
  <div className='terminal-footer-unit terminal-footer-info'>
112
- <Tooltip
113
- title='Terminal Info'
114
- >
115
- <InfoCircleOutlined
116
- onClick={this.handleInfoPanel}
117
- className='pointer font18 terminal-info-icon'
118
- />
119
- </Tooltip>
111
+ <InfoCircleOutlined
112
+ onClick={this.handleInfoPanel}
113
+ className='pointer font18 terminal-info-icon'
114
+ />
120
115
  </div>
121
116
  )
122
117
  }
@@ -9,9 +9,12 @@ import {
9
9
  BorderHorizontalOutlined,
10
10
  CloseSquareFilled,
11
11
  SearchOutlined,
12
- FullscreenOutlined
12
+ FullscreenOutlined,
13
+ PaperClipOutlined
13
14
  } from '@ant-design/icons'
14
- import { Tooltip } from 'antd'
15
+ import {
16
+ Tooltip
17
+ } from 'antd'
15
18
  import { last, findIndex, pick } from 'lodash-es'
16
19
  import generate from '../../common/uid'
17
20
  import copy from 'json-deep-copy'
@@ -72,6 +75,8 @@ export default class SessionWrapper extends Component {
72
75
  this.state = {
73
76
  pid: null,
74
77
  enableSftp: false,
78
+ cwd: '',
79
+ sftpPathFollowSsh: !!props.config.sftpPathFollowSsh,
75
80
  splitDirection: terminalSplitDirectionMap.horizontal,
76
81
  activeSplitId,
77
82
  infoPanelPinned: false,
@@ -89,14 +94,22 @@ export default class SessionWrapper extends Component {
89
94
  // this.initEvent()
90
95
  }
91
96
 
92
- // isActive () {
93
- // const {
94
- // tab,
95
- // currentTabId
96
- // } = this.props
97
- // return currentTabId === tab.id &&
98
- // tab.pane === paneMap.terminal
99
- // }
97
+ setCwd = (cwd, tid) => {
98
+ this.setState(old => {
99
+ return {
100
+ cwd,
101
+ terminals: old.terminals.map(t => {
102
+ if (t.id === tid) {
103
+ return {
104
+ ...t,
105
+ cwd
106
+ }
107
+ }
108
+ return t
109
+ })
110
+ }
111
+ })
112
+ }
100
113
 
101
114
  handleShowInfo = (infoPanelProps) => {
102
115
  this.setState({
@@ -111,6 +124,12 @@ export default class SessionWrapper extends Component {
111
124
  })
112
125
  }
113
126
 
127
+ toggleCheckSftpPathFollowSsh = () => {
128
+ this.setState({
129
+ sftpPathFollowSsh: !this.state.sftpPathFollowSsh
130
+ })
131
+ }
132
+
114
133
  hideInfoPanel = () => {
115
134
  this.setState({
116
135
  showInfo: false
@@ -266,7 +285,8 @@ export default class SessionWrapper extends Component {
266
285
  activeSplitId,
267
286
  splitDirection,
268
287
  sessionOptions,
269
- sessionId
288
+ sessionId,
289
+ sftpPathFollowSsh
270
290
  } = this.state
271
291
  const {
272
292
  pane
@@ -297,6 +317,7 @@ export default class SessionWrapper extends Component {
297
317
  ...this.props,
298
318
  ...t,
299
319
  activeSplitId,
320
+ sftpPathFollowSsh,
300
321
  themeConfig,
301
322
  pane,
302
323
  ...pick(
@@ -308,7 +329,8 @@ export default class SessionWrapper extends Component {
308
329
  'setSessionState',
309
330
  'handleShowInfo',
310
331
  'onChangePane',
311
- 'hideInfoPanel'
332
+ 'hideInfoPanel',
333
+ 'setCwd'
312
334
  ]),
313
335
  ...this.computePosition(t.position / 10)
314
336
  }
@@ -330,22 +352,34 @@ export default class SessionWrapper extends Component {
330
352
  }
331
353
 
332
354
  renderSftp = () => {
333
- const { sessionOptions, sessionId, pid, enableSftp } = this.state
355
+ const {
356
+ sessionOptions,
357
+ sessionId,
358
+ pid,
359
+ enableSftp,
360
+ sftpPathFollowSsh,
361
+ cwd
362
+ } = this.state
334
363
  const { pane } = this.props.tab
335
364
  const height = this.computeHeight()
336
365
  const cls = pane === paneMap.terminal
337
366
  ? 'hide'
338
367
  : ''
368
+ const exts = {
369
+ sftpPathFollowSsh,
370
+ cwd,
371
+ pid,
372
+ enableSftp,
373
+ sessionOptions,
374
+ height,
375
+ sessionId,
376
+ pane,
377
+ ...this.props
378
+ }
339
379
  return (
340
380
  <div className={cls}>
341
381
  <Sftp
342
- pid={pid}
343
- enableSftp={enableSftp}
344
- sessionOptions={sessionOptions}
345
- height={height}
346
- sessionId={sessionId}
347
- pane={pane}
348
- {...this.props}
382
+ {...exts}
349
383
  />
350
384
  </div>
351
385
  )
@@ -365,7 +399,7 @@ export default class SessionWrapper extends Component {
365
399
  renderSearchIcon = () => {
366
400
  const title = e('search')
367
401
  return (
368
- <Tooltip title={title}>
402
+ <Tooltip title={title} placement='bottomLeft'>
369
403
  <SearchOutlined
370
404
  className='mg1r icon-info font16 iblock pointer spliter'
371
405
  onClick={this.handleOpenSearch}
@@ -377,7 +411,7 @@ export default class SessionWrapper extends Component {
377
411
  fullscreenIcon = () => {
378
412
  const title = e('fullscreen')
379
413
  return (
380
- <Tooltip title={title}>
414
+ <Tooltip title={title} placement='bottomLeft'>
381
415
  <FullscreenOutlined
382
416
  className='mg1r icon-info font16 iblock pointer spliter term-fullscreen-control1'
383
417
  onClick={this.handleFullscreen}
@@ -387,7 +421,7 @@ export default class SessionWrapper extends Component {
387
421
  }
388
422
 
389
423
  renderControl = () => {
390
- const { splitDirection, terminals } = this.state
424
+ const { splitDirection, terminals, sftpPathFollowSsh } = this.state
391
425
  const { props } = this
392
426
  const { pane } = props.tab
393
427
  const termType = props.tab?.type
@@ -413,6 +447,16 @@ export default class SessionWrapper extends Component {
413
447
  if (isSsh || isLocal) {
414
448
  controls.push(isSsh ? paneMap.sftp : paneMap.fileManager)
415
449
  }
450
+ const checkTxt = e('sftpPathFollowSsh') + ' [Beta]'
451
+ const checkProps = {
452
+ onClick: this.toggleCheckSftpPathFollowSsh,
453
+ className: classnames(
454
+ 'sftp-follow-ssh-icon',
455
+ {
456
+ active: sftpPathFollowSsh
457
+ }
458
+ )
459
+ }
416
460
  return (
417
461
  <div
418
462
  className='terminal-control fix'
@@ -442,6 +486,17 @@ export default class SessionWrapper extends Component {
442
486
  })
443
487
  }
444
488
  </div>
489
+ {
490
+ isSsh || isLocal
491
+ ? (
492
+ <Tooltip title={checkTxt}>
493
+ <span {...checkProps}>
494
+ <PaperClipOutlined />
495
+ </span>
496
+ </Tooltip>
497
+ )
498
+ : null
499
+ }
445
500
  {
446
501
  pane === paneMap.terminal
447
502
  ? (
@@ -461,6 +516,7 @@ export default class SessionWrapper extends Component {
461
516
  }
462
517
  <Tooltip
463
518
  title={`${e('split')}`}
519
+ placement='bottomLeft'
464
520
  >
465
521
  <Icon1
466
522
  className={cls1}
@@ -469,6 +525,7 @@ export default class SessionWrapper extends Component {
469
525
  </Tooltip>
470
526
  <Tooltip
471
527
  title={e('changeDirection')}
528
+ placement='bottomLeft'
472
529
  >
473
530
  <Icon2
474
531
  className={cls2}
@@ -16,6 +16,7 @@
16
16
  position relative
17
17
  display inline-block
18
18
 
19
+ .sftp-follow-ssh-icon
19
20
  .type-tab
20
21
  display inline-block
21
22
  vertical-align middle
@@ -31,6 +32,7 @@
31
32
  &.active
32
33
  .type-tab-line
33
34
  display inline-block
35
+
34
36
  .is-transporting
35
37
  .type-tab.sftp
36
38
  .type-tab-line
@@ -340,6 +340,8 @@ class Sessions extends Component {
340
340
  currentTabId,
341
341
  tab: toSimpleObj(tab),
342
342
  ...pick(store, [
343
+ 'fileOperation',
344
+ 'file',
343
345
  'height',
344
346
  'width',
345
347
  'activeTerminalId',
@@ -382,7 +384,6 @@ class Sessions extends Component {
382
384
  currentTabId,
383
385
  config,
384
386
  ...pick(store, [
385
- 'fileOperation',
386
387
  'height',
387
388
  'width',
388
389
  'activeTerminalId',
@@ -320,23 +320,16 @@ export default class SettingCommon extends Component {
320
320
  const defaultValue = defaultSettings[name]
321
321
  const onChange = (e) => this.onChangeValue(e.target.value, name)
322
322
  const onChangeArgs = (v) => this.onChangeValue(v, agrsProp)
323
- const style = {
324
- style: {
325
- width: '40%'
326
- }
327
- }
328
323
  const styleArg = {
329
324
  style: {
330
- width: '40%',
331
- marginLeft: '3px'
325
+ width: '40%'
332
326
  }
333
327
  }
334
328
  return (
335
329
  <div className='pd2b'>
336
- <Space.Compact>
330
+ <Space.Compact block>
337
331
  <Input
338
332
  value={value}
339
- {...style}
340
333
  onChange={onChange}
341
334
  placeholder={defaultValue}
342
335
  />
@@ -111,6 +111,12 @@ export default class Sftp extends Component {
111
111
  selectedFiles: []
112
112
  })
113
113
  }
114
+ if (
115
+ this.props.sftpPathFollowSsh &&
116
+ prevProps.cwd !== this.props.cwd
117
+ ) {
118
+ this.updateCwd(this.props.cwd)
119
+ }
114
120
  }
115
121
 
116
122
  componentWillUnmount () {
@@ -196,7 +202,42 @@ export default class Sftp extends Component {
196
202
  this.props.pane === paneMap.fileManager
197
203
  }
198
204
 
205
+ getCwdLocal = () => {
206
+ if (
207
+ !this.shouldRenderRemote() &&
208
+ this.props.sftpPathFollowSsh &&
209
+ this.props.cwd
210
+ ) {
211
+ return this.props.cwd
212
+ }
213
+ }
214
+
215
+ updateCwd = (cwd) => {
216
+ if (!this.state.inited) {
217
+ return
218
+ }
219
+ const type = this.shouldRenderRemote()
220
+ ? typeMap.remote
221
+ : typeMap.local
222
+ // this.setState({
223
+ // [`${type}PathTemp`]: cwd
224
+ // }, () => {
225
+ // this.onGoto(
226
+ // type
227
+ // )
228
+ // })
229
+ const n = `${type}Path`
230
+ const nt = n + 'Temp'
231
+ this.setState({
232
+ [n]: cwd,
233
+ [nt]: cwd
234
+ }, () => this[`${type}List`]())
235
+ }
236
+
199
237
  getPwd = async (username) => {
238
+ if (this.props.sftpPathFollowSsh && this.props.cwd) {
239
+ return this.props.cwd
240
+ }
200
241
  const home = await runCmd(
201
242
  this.props.pid,
202
243
  this.props.sessionId,
@@ -427,16 +468,19 @@ export default class Sftp extends Component {
427
468
  }
428
469
 
429
470
  initData = async () => {
430
- const { props } = this
431
- const host = props.tab?.host &&
432
- props.tab?.type !== terminalSshConfigType &&
433
- props.tab?.type !== terminalSerialType
434
- if (host) {
471
+ if (this.shouldRenderRemote()) {
435
472
  this.initRemoteAll()
436
473
  }
437
474
  this.initLocalAll()
438
475
  }
439
476
 
477
+ shouldRenderRemote = () => {
478
+ const { props } = this
479
+ return props.tab?.host &&
480
+ props.tab?.type !== terminalSshConfigType &&
481
+ props.tab?.type !== terminalSerialType
482
+ }
483
+
440
484
  initLocalAll = () => {
441
485
  this.localListOwner()
442
486
  this.localList()
@@ -699,7 +743,10 @@ export default class Sftp extends Component {
699
743
  )
700
744
  try {
701
745
  const noPathInit = localPathReal || this.state.localPath
702
- const localPath = noPathInit || this.props.tab.startDirectoryLocal || window.pre.homeOrtmp
746
+ const localPath = noPathInit ||
747
+ this.getCwdLocal() ||
748
+ this.props.tab.startDirectoryLocal ||
749
+ window.pre.homeOrTmp
703
750
  const locals = await fs.readdirAsync(localPath)
704
751
  const local = []
705
752
  for (const name of locals) {
@@ -1010,8 +1057,7 @@ export default class Sftp extends Component {
1010
1057
  const {
1011
1058
  height, width
1012
1059
  } = this.props
1013
- const shouldRenderRemote = this.props.tab?.authType &&
1014
- this.props.tab?.enableSftp !== false
1060
+ const shouldRenderRemote = this.shouldRenderRemote()
1015
1061
  if (!shouldRenderRemote) {
1016
1062
  return (
1017
1063
  this.renderSection(arr[0], {
@@ -5,6 +5,7 @@
5
5
 
6
6
  import React from 'react'
7
7
  import { shortcutExtend } from './shortcut-handler.js'
8
+ import { throttle } from 'lodash-es'
8
9
 
9
10
  class ShortcutControl extends React.PureComponent {
10
11
  componentDidMount () {
@@ -40,33 +41,33 @@ class ShortcutControl extends React.PureComponent {
40
41
  x && x.click()
41
42
  }
42
43
 
43
- zoominShortcut = (e) => {
44
+ zoominShortcut = throttle((e) => {
44
45
  e.stopPropagation()
45
46
  window.store.zoom(0.25, true)
46
- }
47
+ }, 1000)
47
48
 
48
- zoomoutShortcut = (e) => {
49
+ zoomoutShortcut = throttle((e) => {
49
50
  e.stopPropagation()
50
51
  window.store.zoom(-0.25, true)
51
- }
52
+ }, 1000)
52
53
 
53
- zoominTerminalShortcut = (event) => {
54
+ zoominTerminalShortcut = throttle((event) => {
54
55
  if (window.store.inActiveTerminal) {
55
56
  window.store.zoomTerminal(event.wheelDeltaY || 120)
56
57
  } else {
57
58
  const plus = 0.2
58
59
  window.store.zoom(plus, true)
59
60
  }
60
- }
61
+ }, 1000)
61
62
 
62
- zoomoutTerminalShortcut = (event) => {
63
+ zoomoutTerminalShortcut = throttle((event) => {
63
64
  if (window.store.inActiveTerminal) {
64
65
  window.store.zoomTerminal(event.wheelDeltaY || -120)
65
66
  } else {
66
67
  const plus = -0.2
67
68
  window.store.zoom(plus, true)
68
69
  }
69
- }
70
+ }, 1000)
70
71
 
71
72
  render () {
72
73
  return null
@@ -53,21 +53,27 @@ export function shortcutExtend (Cls) {
53
53
  shiftKey,
54
54
  metaKey,
55
55
  altKey,
56
- wheelDeltaY
56
+ wheelDeltaY,
57
+ type,
58
+ key
57
59
  } = event
58
- if (this.isTerm) {
59
- const keymap = [
60
- { key: 'Backspace', shiftKey: false, mapCode: 8 },
61
- { key: 'Backspace', shiftKey: true, mapCode: 127 }
62
- ]
63
- if (event.type === 'keydown') {
64
- for (const i in keymap) {
65
- if (keymap[i].key === event.key && keymap[i].shiftKey === shiftKey) {
66
- this.socket.send(String.fromCharCode(keymap[i].mapCode))
67
- return false
68
- }
69
- }
60
+ if (key === 'Backspace' && this.isTerm && type === 'keydown') {
61
+ const now = Date.now()
62
+ if (!this.lastTimePressDel) {
63
+ this.lastTimePressDel = now
70
64
  }
65
+ const timer = now - this.lastTimePressDel
66
+ const count = Math.ceil(timer / 800)
67
+ let char = String.fromCharCode(
68
+ shiftKey ? 127 : 8
69
+ )
70
+ char = new Array(count).fill(char).join('')
71
+ this.socket.send(
72
+ char
73
+ )
74
+ return false
75
+ } else if (key === 'Backspace' && this.isTerm && type === 'keyup') {
76
+ delete this.lastTimePressDel
71
77
  }
72
78
  const codeName = event instanceof window.WheelEvent
73
79
  ? (wheelDeltaY > 0 ? 'mouseWheelUp' : 'mouseWheelDown')
@@ -1,3 +1,5 @@
1
+ import { useEffect } from 'react'
2
+
1
3
  export default function AppDrag (props) {
2
4
  function canOperate (e) {
3
5
  const {
@@ -37,6 +39,10 @@ export default function AppDrag (props) {
37
39
  window.pre.runGlobalAsync('maximize')
38
40
  }
39
41
  }
42
+
43
+ useEffect(() => {
44
+ window.addEventListener('contextmenu', onMouseUp)
45
+ }, [])
40
46
  return (
41
47
  <div
42
48
  className='app-drag'
@@ -219,7 +219,19 @@ export default class Tabs extends React.Component {
219
219
  )
220
220
  }
221
221
 
222
- render () {
222
+ renderContent () {
223
+ const { config } = this.props
224
+ if (config.useSystemTitleBar) {
225
+ return this.renderContentInner()
226
+ }
227
+ return (
228
+ <AppDrag>
229
+ {this.renderContentInner()}
230
+ </AppDrag>
231
+ )
232
+ }
233
+
234
+ renderContentInner () {
223
235
  const { tabs = [], width } = this.props
224
236
  const len = tabs.length
225
237
  const tabsWidthAll = tabMargin * len + 10 + this.tabsWidth()
@@ -230,46 +242,51 @@ export default class Tabs extends React.Component {
230
242
  const style = {
231
243
  width: width - windowControlWidth - 86
232
244
  }
245
+ return (
246
+ <div
247
+ className='tabs-inner'
248
+ style={style}
249
+ >
250
+ <div
251
+ style={{
252
+ left
253
+ }}
254
+ />
255
+ <div
256
+ className='tabs-wrapper relative'
257
+ style={{
258
+ width: tabsWidthAll + extraTabWidth + 10
259
+ }}
260
+ onDoubleClick={this.handleAdd}
261
+ >
262
+ {
263
+ tabs.map((tab, i) => {
264
+ const isLast = i === len - 1
265
+ return (
266
+ <Tab
267
+ {...this.props}
268
+ tab={tab}
269
+ isLast={isLast}
270
+ key={tab.id}
271
+ />
272
+ )
273
+ })
274
+ }
275
+ {
276
+ !overflow
277
+ ? this.renderAddBtn()
278
+ : null
279
+ }
280
+ </div>
281
+ </div>
282
+ )
283
+ }
284
+
285
+ render () {
286
+ const overflow = this.isOverflow()
233
287
  return (
234
288
  <div className='tabs' ref={this.tabsRef}>
235
- <AppDrag>
236
- <div
237
- className='tabs-inner'
238
- style={style}
239
- >
240
- <div
241
- style={{
242
- left
243
- }}
244
- />
245
- <div
246
- className='tabs-wrapper relative'
247
- style={{
248
- width: tabsWidthAll + extraTabWidth + 10
249
- }}
250
- onDoubleClick={this.handleAdd}
251
- >
252
- {
253
- tabs.map((tab, i) => {
254
- const isLast = i === len - 1
255
- return (
256
- <Tab
257
- {...this.props}
258
- tab={tab}
259
- isLast={isLast}
260
- key={tab.id}
261
- />
262
- )
263
- })
264
- }
265
- {
266
- !overflow
267
- ? this.renderAddBtn()
268
- : null
269
- }
270
- </div>
271
- </div>
272
- </AppDrag>
289
+ {this.renderContent()}
273
290
  <WindowControl
274
291
  store={window.store}
275
292
  />
@@ -2,6 +2,7 @@
2
2
  * customize AttachAddon
3
3
  */
4
4
  import { AttachAddon } from 'xterm-addon-attach'
5
+ import strip from '@electerm/strip-ansi'
5
6
 
6
7
  export default class AttachAddonCustom extends AttachAddon {
7
8
  constructor (term, options, encode, isWindowsShell) {
@@ -22,7 +23,31 @@ export default class AttachAddonCustom extends AttachAddon {
22
23
  const fileReader = new FileReader()
23
24
  fileReader.addEventListener('load', () => {
24
25
  const str = this.decoder.decode(fileReader.result)
25
- terminal.write(str)
26
+ if (terminal.parent.props.sftpPathFollowSsh && terminal.buffer.active.type !== 'alternate') {
27
+ const {
28
+ cwdId
29
+ } = terminal
30
+ const nss = str.split('\r')
31
+ const nnss = []
32
+ for (const str1 of nss) {
33
+ const ns = strip(str1).trim()
34
+ if (ns.includes(cwdId) && ns.includes('$PWD')) {
35
+ nnss.push(str1.replace(`echo "${cwdId}$PWD"`, ''))
36
+ } else if (
37
+ (cwdId && ns.startsWith(cwdId))
38
+ ) {
39
+ delete terminal.cwdId
40
+ const cwd = ns.replace(cwdId, '').trim()
41
+ terminal.parent.setCwd(cwd)
42
+ nnss.push('\x1b[2A\x1b[0J')
43
+ } else {
44
+ nnss.push(str1)
45
+ }
46
+ }
47
+ terminal.write(nnss.join('\r'))
48
+ } else {
49
+ terminal.write(str)
50
+ }
26
51
  })
27
52
  fileReader.readAsArrayBuffer(new window.Blob([data]))
28
53
  }
@@ -25,7 +25,9 @@ import {
25
25
  transferTypeMap,
26
26
  terminalActions,
27
27
  commonActions,
28
- rendererTypes
28
+ rendererTypes,
29
+ cwdId,
30
+ isMac
29
31
  } from '../../common/constants'
30
32
  import deepCopy from 'json-deep-copy'
31
33
  import { readClipboardAsync, copy } from '../../common/clipboard'
@@ -76,8 +78,6 @@ class Term extends Component {
76
78
 
77
79
  isTerm = true
78
80
 
79
- dataCache = ''
80
-
81
81
  componentDidMount () {
82
82
  this.initTerminal()
83
83
  this.initEvt()
@@ -130,6 +130,7 @@ class Term extends Component {
130
130
  }
131
131
 
132
132
  componentWillUnmount () {
133
+ delete this.term.parent
133
134
  Object.keys(this.timers).forEach(k => {
134
135
  clearTimeout(this.timers[k])
135
136
  })
@@ -242,6 +243,19 @@ class Term extends Component {
242
243
  this.tryInsertSelected()
243
244
  }
244
245
 
246
+ pasteShortcut = (e) => {
247
+ if (isMac) {
248
+ return true
249
+ }
250
+ if (!this.isRemote()) {
251
+ return true
252
+ }
253
+ if (this.term.buffer.active.type !== 'alternate') {
254
+ return false
255
+ }
256
+ return true
257
+ }
258
+
245
259
  showNormalBufferShortcut = (e) => {
246
260
  e.stopPropagation()
247
261
  this.openNormalBuffer()
@@ -762,8 +776,22 @@ class Term extends Component {
762
776
  const str = this.serializeAddon.serialize()
763
777
  const arr = strip(str).split(/ +/)
764
778
  const len = arr.length
765
- const last = arr[len - 1]
766
- this.dataCache = last
779
+ return arr[len - 1]
780
+ }
781
+
782
+ getCwd = () => {
783
+ if (
784
+ this.props.sftpPathFollowSsh &&
785
+ this.term.buffer.active.type !== 'alternate'
786
+ ) {
787
+ const cmd = `\recho "${cwdId}$PWD"\r`
788
+ this.term.cwdId = cwdId
789
+ this.socket.send(cmd)
790
+ }
791
+ }
792
+
793
+ setCwd = (cwd) => {
794
+ this.props.setCwd(cwd, this.state.id)
767
795
  }
768
796
 
769
797
  onData = (d) => {
@@ -771,9 +799,10 @@ class Term extends Component {
771
799
  if (!d.includes('\r')) {
772
800
  delete this.userTypeExit
773
801
  } else {
774
- this.getCmd()
775
- const data = this.dataCache
776
- this.dataCache = ''
802
+ const data = this.getCmd()
803
+ if (this.term.buffer.active.type !== 'alternate') {
804
+ setTimeout(this.getCwd, 200)
805
+ }
777
806
  const exitCmds = [
778
807
  'exit',
779
808
  'logout'
@@ -822,6 +851,7 @@ class Term extends Component {
822
851
 
823
852
  // term.onLineFeed(this.onLineFeed)
824
853
  // term.onTitleChange(this.onTitleChange)
854
+ term.parent = this
825
855
  term.onSelectionChange(this.onSelection)
826
856
  term.open(document.getElementById(id), true)
827
857
  this.loadRenderer(term, config)
@@ -48,6 +48,7 @@ export default Store => {
48
48
 
49
49
  Store.prototype.onBlur = function () {
50
50
  window.focused = false
51
+ window.pre.runSync('windowMove', false)
51
52
  }
52
53
 
53
54
  Store.prototype.selectall = function () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "1.37.81",
3
+ "version": "1.37.92",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",
@@ -1,25 +0,0 @@
1
- /**
2
- * show terminal info
3
- * inluding id log path, and system info for remote session
4
- */
5
-
6
- import { InfoCircleOutlined } from '@ant-design/icons'
7
-
8
- import { Tooltip } from 'antd'
9
- import './terminal-info.styl'
10
-
11
- export default function TerminalInfoIndex (props) {
12
- const pops = {
13
- onClick: props.showInfoPanel,
14
- className: 'pointer font18 terminal-info-icon'
15
- }
16
- return (
17
- <Tooltip
18
- title='Terminal Info'
19
- >
20
- <InfoCircleOutlined
21
- {...pops}
22
- />
23
- </Tooltip>
24
- )
25
- }