@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.
- package/client/common/pre.js +38 -11
- package/client/components/bookmark-form/config/rdp.js +1 -0
- package/client/components/bookmark-form/config/vnc.js +5 -0
- package/client/components/common/remote-float-control.jsx +79 -0
- package/client/components/common/remote-float-control.styl +28 -0
- package/client/components/layout/layout.jsx +2 -1
- package/client/components/main/main.jsx +3 -6
- package/client/components/main/term-fullscreen.styl +1 -10
- package/client/components/rdp/rdp-session.jsx +113 -4
- package/client/components/rdp/resolutions.js +6 -0
- package/client/components/session/session.jsx +4 -3
- package/client/components/session/session.styl +18 -5
- package/client/components/session/sessions.jsx +2 -1
- package/client/components/shortcuts/shortcut-control.jsx +5 -3
- package/client/components/shortcuts/shortcut-handler.js +4 -2
- package/client/components/terminal/attach-addon-custom.js +13 -0
- package/client/components/terminal/event-emitter.js +27 -0
- package/client/components/terminal/terminal.jsx +10 -297
- package/client/components/terminal/zmodem-client.js +385 -0
- package/client/components/terminal-info/data-cols-parser.jsx +3 -2
- package/client/components/terminal-info/network.jsx +3 -2
- package/client/components/vnc/vnc-session.jsx +397 -52
- package/client/css/basic.styl +3 -0
- package/client/store/event.js +2 -2
- package/client/store/init-state.js +1 -1
- package/package.json +1 -1
- package/client/common/byte-format.js +0 -14
- package/client/components/main/term-fullscreen-control.jsx +0 -21
- 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 {
|
|
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.
|
|
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.
|
|
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>
|