@electerm/electerm-react 2.7.9 → 2.8.6

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 (29) hide show
  1. package/client/common/pre.js +38 -11
  2. package/client/components/bookmark-form/config/rdp.js +1 -0
  3. package/client/components/bookmark-form/config/vnc.js +5 -0
  4. package/client/components/common/remote-float-control.jsx +79 -0
  5. package/client/components/common/remote-float-control.styl +28 -0
  6. package/client/components/layout/layout.jsx +2 -1
  7. package/client/components/main/main.jsx +3 -6
  8. package/client/components/main/term-fullscreen.styl +1 -10
  9. package/client/components/rdp/rdp-session.jsx +113 -4
  10. package/client/components/rdp/resolutions.js +6 -0
  11. package/client/components/session/session.jsx +4 -3
  12. package/client/components/session/session.styl +18 -5
  13. package/client/components/session/sessions.jsx +2 -1
  14. package/client/components/shortcuts/shortcut-control.jsx +5 -3
  15. package/client/components/shortcuts/shortcut-handler.js +4 -2
  16. package/client/components/terminal/attach-addon-custom.js +13 -0
  17. package/client/components/terminal/event-emitter.js +27 -0
  18. package/client/components/terminal/terminal.jsx +10 -297
  19. package/client/components/terminal/zmodem-client.js +385 -0
  20. package/client/components/terminal-info/data-cols-parser.jsx +3 -2
  21. package/client/components/terminal-info/network.jsx +3 -2
  22. package/client/components/vnc/vnc-session.jsx +397 -52
  23. package/client/css/basic.styl +3 -0
  24. package/client/store/event.js +2 -2
  25. package/client/store/init-state.js +1 -1
  26. package/package.json +1 -1
  27. package/client/common/byte-format.js +0 -14
  28. package/client/components/main/term-fullscreen-control.jsx +0 -21
  29. package/client/components/terminal/xterm-zmodem.js +0 -55
@@ -1,6 +1,5 @@
1
1
  import { Component, createRef } from 'react'
2
2
  import { handleErr } from '../../common/fetch.jsx'
3
- import generate from '../../common/uid.js'
4
3
  import { isEqual, pick, debounce, throttle } from 'lodash-es'
5
4
  import clone from '../../common/to-simple-obj.js'
6
5
  import resolve from '../../common/resolve.js'
@@ -22,10 +21,8 @@ import {
22
21
  paneMap,
23
22
  typeMap,
24
23
  isWin,
25
- transferTypeMap,
26
24
  rendererTypes,
27
- isMac,
28
- zmodemTransferPackSize
25
+ isMac
29
26
  } from '../../common/constants.js'
30
27
  import deepCopy from 'json-deep-copy'
31
28
  import { readClipboardAsync, readClipboard, copy } from '../../common/clipboard.js'
@@ -37,7 +34,7 @@ import { CanvasAddon } from '@xterm/addon-canvas'
37
34
  import { WebglAddon } from '@xterm/addon-webgl'
38
35
  import { LigaturesAddon } from '@xterm/addon-ligatures'
39
36
  import getProxy from '../../common/get-proxy.js'
40
- import { AddonZmodem } from './xterm-zmodem.js'
37
+ import { ZmodemClient } from './zmodem-client.js'
41
38
  import { Unicode11Addon } from '@xterm/addon-unicode11'
42
39
  import keyControlPressed from '../../common/key-control-pressed.js'
43
40
  import { Terminal } from '@xterm/xterm'
@@ -45,22 +42,20 @@ import NormalBuffer from './normal-buffer.jsx'
45
42
  import { createTerm, resizeTerm } from './terminal-apis.js'
46
43
  import { shortcutExtend, shortcutDescExtend } from '../shortcuts/shortcut-handler.js'
47
44
  import { KeywordHighlighterAddon } from './highlight-addon.js'
48
- import { getLocalFileInfo } from '../sftp/file-read.js'
49
45
  import { getFilePath, isUnsafeFilename } from '../../common/file-drop-utils.js'
50
46
  import { CommandTrackerAddon } from './command-tracker-addon.js'
51
47
  import AIIcon from '../icons/ai-icon.jsx'
52
- import { formatBytes } from '../../common/byte-format.js'
53
48
  import {
54
49
  getShellIntegrationCommand,
55
50
  detectRemoteShell,
56
51
  detectShellType
57
52
  } from './shell.js'
58
- import * as fs from './fs.js'
59
53
  import iconsMap from '../sys-menu/icons-map.jsx'
60
54
  import { refs, refsStatic } from '../common/ref.js'
61
55
  import ExternalLink from '../common/external-link.jsx'
62
56
  import createDefaultLogPath from '../../common/default-log-path.js'
63
57
  import SearchResultBar from './terminal-search-bar'
58
+ import RemoteFloatControl from '../common/remote-float-control'
64
59
 
65
60
  const e = window.translate
66
61
 
@@ -143,9 +138,6 @@ class Term extends Component {
143
138
  if (window.store.activeTerminalId === this.props.tab.id) {
144
139
  window.store.activeTerminalId = ''
145
140
  }
146
- if (this.zsession) {
147
- this.onZmodemEnd()
148
- }
149
141
  this.term.parent = null
150
142
  Object.keys(this.timers).forEach(k => {
151
143
  clearTimeout(this.timers[k])
@@ -162,7 +154,7 @@ class Term extends Component {
162
154
  }
163
155
  this.attachAddon = null
164
156
  this.fitAddon = null
165
- this.zmodemAddon = null
157
+ this.zmodemClient = null
166
158
  this.searchAddon = null
167
159
  this.fitAddon = null
168
160
  this.cmdAddon = null
@@ -464,287 +456,6 @@ class Term extends Component {
464
456
  }
465
457
  }
466
458
 
467
- onzmodemRetract = () => {
468
- console.debug('zmodemRetract')
469
- }
470
-
471
- writeBanner = (type) => {
472
- const border = '='.repeat(50)
473
- this.term.write(`\r\n${border}\r\n`)
474
- this.term.write('\x1b[33m\x1b[1mRecommend use trzsz instead: https://github.com/trzsz/trzsz\x1b[0m\r\n')
475
- this.term.write(`${border}\r\n\r\n`)
476
- this.term.write(`\x1b[32m\x1b[1mZMODEM::${type}::START\x1b[0m\r\n`)
477
- }
478
-
479
- onReceiveZmodemSession = async () => {
480
- const savePath = await this.openSaveFolderSelect()
481
- this.zsession.on('offer', this.onOfferReceive)
482
- this.zsession.start()
483
- this.term.write('\r\n\x1b[2A\r\n')
484
- if (!savePath) {
485
- return this.onZmodemEnd()
486
- }
487
- this.writeBanner('RECEIVE')
488
- this.zmodemSavePath = savePath
489
- return new Promise((resolve) => {
490
- this.zsession.on('session_end', resolve)
491
- })
492
- .then(this.onZmodemEnd)
493
- .catch(this.onZmodemCatch)
494
- }
495
-
496
- initZmodemDownload = async (name, size) => {
497
- if (!this.zmodemSavePath) {
498
- return
499
- }
500
- let pth = window.pre.resolve(
501
- this.zmodemSavePath, name
502
- )
503
- const exist = await fs.exists(pth).catch(() => false)
504
- if (exist) {
505
- pth = pth + '.' + generate()
506
- }
507
- const fd = await fs.open(pth, 'w').catch(this.onZmodemEnd)
508
- this.downloadFd = fd
509
- this.downloadPath = pth
510
- this.downloadCount = 0
511
- this.zmodemStartTime = Date.now()
512
- this.downloadSize = size
513
- this.updateZmodemProgress(
514
- 0, pth, size, transferTypeMap.download
515
- )
516
- return fd
517
- }
518
-
519
- onOfferReceive = async (xfer) => {
520
- const {
521
- name,
522
- size
523
- } = xfer.get_details()
524
- if (!this.downloadFd) {
525
- await this.initZmodemDownload(name, size)
526
- }
527
- xfer.on('input', this.onZmodemDownload)
528
- this.xfer = xfer
529
- await xfer.accept()
530
- .then(this.finishZmodemTransfer)
531
- .catch(this.onZmodemEnd)
532
- }
533
-
534
- checkCache = async () => {
535
- if (this.DownloadCache?.length > 0) {
536
- return fs.write(this.downloadFd, new Uint8Array(this.DownloadCache))
537
- }
538
- }
539
-
540
- onZmodemDownload = async payload => {
541
- if (this.onCanceling || !this.downloadFd) {
542
- return
543
- }
544
- // if (!this.DownloadCache) {
545
- // this.DownloadCache = []
546
- // }
547
- // this.DownloadCache = this.DownloadCache.concat(payload)
548
- // this.downloadCount += payload.length
549
- // if (this.DownloadCache.length < zmodemTransferPackSize) {
550
- // return this.updateZmodemProgress(
551
- // this.downloadCount,
552
- // this.downloadPath,
553
- // this.downloadSize,
554
- // transferTypeMap.download
555
- // )
556
- // }
557
- // this.writeCache = this.DownloadCache
558
- // this.DownloadCache = []
559
- this.downloadCount += payload.length
560
- await fs.write(this.downloadFd, new Uint8Array(payload))
561
- this.updateZmodemProgress(
562
- this.downloadCount,
563
- this.downloadPath,
564
- this.downloadSize,
565
- transferTypeMap.download
566
- )
567
- }
568
-
569
- updateZmodemProgress = throttle((start, name, size, type) => {
570
- this.zmodemTransfer = {
571
- type,
572
- start,
573
- name,
574
- size
575
- }
576
- this.writeZmodemProgress()
577
- }, 500)
578
-
579
- finishZmodemTransfer = () => {
580
- this.zmodemTransfer = {
581
- ...this.zmodemTransfer,
582
- start: this.zmodemTransfer.size
583
- }
584
- this.writeZmodemProgress()
585
- }
586
-
587
- writeZmodemProgress = () => {
588
- if (this.onCanceling) {
589
- return
590
- }
591
- const {
592
- size, start, name
593
- } = this.zmodemTransfer
594
- const speed = size > 0 ? formatBytes(start * 1000 / 1024 / (Date.now() - this.zmodemStartTime)) : 0
595
- const percent = size > 0 ? Math.floor(start * 100 / size) : 100
596
- const str = `\x1b[32m${name}\x1b[0m::${percent}%,${start}/${size},${speed}/s`
597
- this.term.write('\r\n\x1b[2A' + str + '\n')
598
- }
599
-
600
- zmodemTransferFile = async (file, filesRemaining, sizeRemaining) => {
601
- const offer = {
602
- obj: file,
603
- name: file.name,
604
- size: file.size,
605
- files_remaining: filesRemaining,
606
- bytes_remaining: sizeRemaining
607
- }
608
- const xfer = await this.zsession.send_offer(offer)
609
- if (!xfer) {
610
- this.onZmodemEnd()
611
- return window.store.onError(new Error('Transfer cancelled, maybe file already exists'))
612
- }
613
- this.zmodemStartTime = Date.now()
614
- const fd = await fs.open(file.filePath, 'r')
615
- let start = 0
616
- const { size } = file
617
- let inited = false
618
- while (start < size || !inited) {
619
- const rest = size - start
620
- const len = rest > zmodemTransferPackSize ? zmodemTransferPackSize : rest
621
- const buffer = new Uint8Array(len)
622
- const newArr = await fs.read(fd, buffer, 0, len, null)
623
- const n = newArr.length
624
- await xfer.send(newArr)
625
- start = start + n
626
- inited = true
627
- this.updateZmodemProgress(start, file.name, size, transferTypeMap.upload)
628
- if (n < zmodemTransferPackSize || start >= file.size || this.onCanceling) {
629
- break
630
- }
631
- }
632
- await fs.close(fd)
633
- this.finishZmodemTransfer()
634
- await xfer.end()
635
- }
636
-
637
- openFileSelect = async () => {
638
- const properties = [
639
- 'openFile',
640
- 'multiSelections',
641
- 'showHiddenFiles',
642
- 'noResolveAliases',
643
- 'treatPackageAsDirectory',
644
- 'dontAddToRecent'
645
- ]
646
- const files = await window.api.openDialog({
647
- title: 'Choose some files to send',
648
- message: 'Choose some files to send',
649
- properties
650
- }).catch(() => false)
651
- if (!files || !files.length) {
652
- return this.onZmodemEnd()
653
- }
654
- const r = []
655
- for (const filePath of files) {
656
- const stat = await getLocalFileInfo(filePath)
657
- r.push({ ...stat, filePath })
658
- }
659
- return r
660
- }
661
-
662
- openSaveFolderSelect = async () => {
663
- const savePaths = await window.api.openDialog({
664
- title: 'Choose a folder to save file(s)',
665
- message: 'Choose a folder to save file(s)',
666
- properties: [
667
- 'openDirectory',
668
- 'showHiddenFiles',
669
- 'createDirectory',
670
- 'noResolveAliases',
671
- 'treatPackageAsDirectory',
672
- 'dontAddToRecent'
673
- ]
674
- }).catch(() => false)
675
- if (!savePaths || !savePaths.length) {
676
- return false
677
- }
678
- return savePaths[0]
679
- }
680
-
681
- beforeZmodemUpload = async (files) => {
682
- if (!files || !files.length) {
683
- return false
684
- }
685
- this.writeBanner('SEND')
686
- let filesRemaining = files.length
687
- let sizeRemaining = files.reduce((a, b) => a + b.size, 0)
688
- for (const f of files) {
689
- await this.zmodemTransferFile(f, filesRemaining, sizeRemaining)
690
- filesRemaining = filesRemaining - 1
691
- sizeRemaining = sizeRemaining - f.size
692
- }
693
- this.onZmodemEnd()
694
- }
695
-
696
- onSendZmodemSession = async () => {
697
- this.term.write('\r\n\x1b[2A\n')
698
- const files = await this.openFileSelect()
699
- this.beforeZmodemUpload(files)
700
- }
701
-
702
- onZmodemEnd = async () => {
703
- this.zmodemSavePath = null
704
- this.onCanceling = true
705
- if (this.downloadFd) {
706
- await fs.close(this.downloadFd)
707
- }
708
- if (this.xfer && this.xfer.end) {
709
- await this.xfer.end().catch(
710
- console.error
711
- )
712
- }
713
- this.xfer = null
714
- if (this.zsession && this.zsession.close) {
715
- await this.zsession.close().catch(
716
- console.error
717
- )
718
- }
719
- this.zsession = null
720
- this.term.focus()
721
- this.term.write('\r\n')
722
- this.onZmodem = false
723
- this.downloadFd = null
724
- // delete this.downloadPath
725
- // delete this.downloadCount
726
- // delete this.downloadSize
727
- this.DownloadCache = null
728
- }
729
-
730
- onZmodemCatch = (e) => {
731
- window.store.onError(e)
732
- this.onZmodemEnd()
733
- }
734
-
735
- onZmodemDetect = detection => {
736
- this.onCanceling = false
737
- this.term.blur()
738
- this.onZmodem = true
739
- const zsession = detection.confirm()
740
- this.zsession = zsession
741
- if (zsession.type === 'receive') {
742
- this.onReceiveZmodemSession()
743
- } else {
744
- this.onSendZmodemSession()
745
- }
746
- }
747
-
748
459
  onContextMenuInner = e => {
749
460
  e.preventDefault()
750
461
  if (this.state.loading) {
@@ -1423,10 +1134,9 @@ class Term extends Component {
1423
1134
  }
1424
1135
  term.loadAddon(new WebLinksAddon(this.webLinkHandler))
1425
1136
  term.focus()
1426
- this.zmodemAddon = new AddonZmodem()
1137
+ this.zmodemClient = new ZmodemClient(this)
1138
+ this.zmodemClient.init(socket)
1427
1139
  this.fitAddon.fit()
1428
- term.loadAddon(this.zmodemAddon)
1429
- term.zmodemAttach(this)
1430
1140
  term.displayRaw = displayRaw
1431
1141
  term.loadAddon(
1432
1142
  new KeywordHighlighterAddon(keywords)
@@ -1583,7 +1293,7 @@ class Term extends Component {
1583
1293
 
1584
1294
  render () {
1585
1295
  const { loading } = this.state
1586
- const { height, width, left, top } = this.props
1296
+ const { height, width, left, top, fullscreen } = this.props
1587
1297
  const { id } = this.props.tab
1588
1298
  const isActive = this.isActiveTerminal()
1589
1299
  const cls = classnames(
@@ -1648,6 +1358,9 @@ class Term extends Component {
1648
1358
  close={this.closeNormalBuffer}
1649
1359
  />
1650
1360
  <SearchResultBar {...barProps} />
1361
+ <RemoteFloatControl
1362
+ isFullScreen={fullscreen}
1363
+ />
1651
1364
  <Spin className='loading-wrapper' spinning={loading} />
1652
1365
  </div>
1653
1366
  </Dropdown>