@electerm/electerm-react 1.37.126 → 1.38.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/constants.js +2 -1
- package/client/common/default-setting.js +1 -1
- package/client/common/pre.js +1 -1
- package/client/components/batch-op/batch-op.jsx +7 -1
- package/client/components/common/logo-elem.jsx +4 -3
- package/client/components/common/logo.styl +9 -0
- package/client/components/footer/footer-entry.jsx +1 -1
- package/client/components/footer/footer.styl +11 -2
- package/client/components/main/main.jsx +1 -0
- package/client/components/main/wrapper.styl +0 -2
- package/client/components/quick-commands/qm.styl +3 -0
- package/client/components/quick-commands/quick-commands-box.jsx +1 -1
- package/client/components/quick-commands/quick-commands-select.jsx +3 -1
- package/client/components/session/session.jsx +7 -1
- package/client/components/session/sessions.jsx +7 -0
- package/client/components/setting-panel/setting-common.jsx +5 -5
- package/client/components/setting-panel/setting-modal.jsx +3 -1
- package/client/components/setting-panel/setting-wrap.jsx +10 -0
- package/client/components/setting-panel/setting-wrap.styl +28 -1
- package/client/components/setting-panel/setting.styl +8 -1
- package/client/components/shortcuts/shortcut-handler.js +11 -0
- package/client/components/shortcuts/shortcuts-defaults.js +5 -0
- package/client/components/sidebar/sidebar.styl +4 -1
- package/client/components/tabs/index.jsx +9 -2
- package/client/components/tabs/tab.jsx +17 -6
- package/client/components/tabs/tabs.styl +13 -6
- package/client/components/tabs/window-control.jsx +4 -1
- package/client/components/terminal/attach-addon-custom.js +11 -2
- package/client/components/terminal/fs.js +59 -0
- package/client/components/terminal/index.jsx +245 -126
- package/client/components/terminal/xterm-zmodem.js +40 -33
- package/client/css/basic.styl +1 -1
- package/client/css/mobile.styl +8 -0
- package/client/entry/basic.js +1 -0
- package/client/store/index.js +0 -5
- package/client/store/init-state.js +2 -1
- package/package.json +1 -1
- package/client/components/terminal/zmodem-transfer.jsx +0 -98
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export const open = (filePath, flag) => {
|
|
2
|
+
const fs = window.require('fs')
|
|
3
|
+
return new Promise((resolve, reject) => {
|
|
4
|
+
fs.open(filePath, flag, (err, fd) => {
|
|
5
|
+
if (err) {
|
|
6
|
+
return reject(err)
|
|
7
|
+
}
|
|
8
|
+
return resolve(fd)
|
|
9
|
+
})
|
|
10
|
+
})
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const close = (fd) => {
|
|
14
|
+
const fs = window.require('fs')
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
fs.close(fd, (err) => {
|
|
17
|
+
if (err) {
|
|
18
|
+
return reject(err)
|
|
19
|
+
}
|
|
20
|
+
return resolve(true)
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const exists = (filePath) => {
|
|
26
|
+
const fs = window.require('fs')
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
fs.access(filePath, (err) => {
|
|
29
|
+
if (err) {
|
|
30
|
+
return reject(err)
|
|
31
|
+
}
|
|
32
|
+
return resolve(true)
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const read = (fd, buffer, offset, length, position) => {
|
|
38
|
+
const fs = window.require('fs')
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
fs.read(fd, buffer, offset, length, position, (err, bytesRead, buffer) => {
|
|
41
|
+
if (err) {
|
|
42
|
+
return reject(err)
|
|
43
|
+
}
|
|
44
|
+
return resolve(buffer.subarray(0, bytesRead))
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const write = (fd, buffer) => {
|
|
50
|
+
const fs = window.require('fs')
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
fs.write(fd, buffer, (err, buffer) => {
|
|
53
|
+
if (err) {
|
|
54
|
+
return reject(err)
|
|
55
|
+
}
|
|
56
|
+
return resolve(buffer)
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Component } from 'react'
|
|
2
|
-
import ZmodemTransfer from './zmodem-transfer'
|
|
3
2
|
import { handleErr } from '../../common/fetch'
|
|
4
3
|
import generate from '../../common/uid'
|
|
5
4
|
import { isEqual, pick, debounce, throttle } from 'lodash-es'
|
|
@@ -27,7 +26,8 @@ import {
|
|
|
27
26
|
commonActions,
|
|
28
27
|
rendererTypes,
|
|
29
28
|
cwdId,
|
|
30
|
-
isMac
|
|
29
|
+
isMac,
|
|
30
|
+
zmodemTransferPackSize
|
|
31
31
|
} from '../../common/constants'
|
|
32
32
|
import deepCopy from 'json-deep-copy'
|
|
33
33
|
import { readClipboardAsync, copy } from '../../common/clipboard'
|
|
@@ -39,7 +39,7 @@ import { CanvasAddon } from 'xterm-addon-canvas'
|
|
|
39
39
|
import { WebglAddon } from 'xterm-addon-webgl'
|
|
40
40
|
import { LigaturesAddon } from 'xterm-addon-ligatures'
|
|
41
41
|
import getProxy from '../../common/get-proxy'
|
|
42
|
-
import {
|
|
42
|
+
import { AddonZmodem } from './xterm-zmodem'
|
|
43
43
|
import { Unicode11Addon } from 'xterm-addon-unicode11'
|
|
44
44
|
import keyControlPressed from '../../common/key-control-pressed'
|
|
45
45
|
import { Terminal } from 'xterm'
|
|
@@ -47,8 +47,11 @@ import NormalBuffer from './normal-buffer'
|
|
|
47
47
|
import { createTerm, resizeTerm } from './terminal-apis'
|
|
48
48
|
import { shortcutExtend, shortcutDescExtend } from '../shortcuts/shortcut-handler.js'
|
|
49
49
|
import { KeywordHighlighterAddon } from './highlight-addon.js'
|
|
50
|
+
import { getLocalFileInfo } from '../sftp/file-read.js'
|
|
50
51
|
import { SerializeAddon } from 'xterm-addon-serialize'
|
|
51
52
|
import strip from '@electerm/strip-ansi'
|
|
53
|
+
import { formatBytes } from '../../common/byte-format.js'
|
|
54
|
+
import * as fs from './fs.js'
|
|
52
55
|
|
|
53
56
|
const { prefix } = window
|
|
54
57
|
const e = prefix('ssh')
|
|
@@ -71,7 +74,6 @@ class Term extends Component {
|
|
|
71
74
|
saveTerminalLogToFile: !!this.props.config.saveTerminalLogToFile,
|
|
72
75
|
addTimeStampToTermLog: !!this.props.config.addTimeStampToTermLog,
|
|
73
76
|
passType: 'password',
|
|
74
|
-
zmodemTransfer: null,
|
|
75
77
|
lines: []
|
|
76
78
|
}
|
|
77
79
|
}
|
|
@@ -128,6 +130,9 @@ class Term extends Component {
|
|
|
128
130
|
}
|
|
129
131
|
|
|
130
132
|
componentWillUnmount () {
|
|
133
|
+
if (this.zsession) {
|
|
134
|
+
this.onZmodemEnd()
|
|
135
|
+
}
|
|
131
136
|
delete this.term.parent
|
|
132
137
|
Object.keys(this.timers).forEach(k => {
|
|
133
138
|
clearTimeout(this.timers[k])
|
|
@@ -161,6 +166,21 @@ class Term extends Component {
|
|
|
161
166
|
}
|
|
162
167
|
]
|
|
163
168
|
|
|
169
|
+
initAttachAddon = (encode) => {
|
|
170
|
+
this.attachAddon = new AttachAddon(
|
|
171
|
+
this.socket,
|
|
172
|
+
undefined,
|
|
173
|
+
this.props.tab.encode,
|
|
174
|
+
isWin && !this.isRemote()
|
|
175
|
+
)
|
|
176
|
+
if (encode || this.decode) {
|
|
177
|
+
this.attachAddon.decoder = encode
|
|
178
|
+
? new TextDecoder(encode)
|
|
179
|
+
: this.decode
|
|
180
|
+
}
|
|
181
|
+
this.term.loadAddon(this.attachAddon)
|
|
182
|
+
}
|
|
183
|
+
|
|
164
184
|
getValue = (props, type, name) => {
|
|
165
185
|
return type === 'glob'
|
|
166
186
|
? props.config[name]
|
|
@@ -420,152 +440,259 @@ class Term extends Component {
|
|
|
420
440
|
log.debug('zmodemRetract')
|
|
421
441
|
}
|
|
422
442
|
|
|
423
|
-
|
|
424
|
-
// * zmodem transfer
|
|
425
|
-
// * then run rz to send from your browser or
|
|
426
|
-
// * sz <file> to send from the remote peer.
|
|
443
|
+
writeBanner = (type) => {
|
|
427
444
|
this.term.write('\r\nRecommmend use trzsz instead: https://github.com/trzsz/trzsz\r\n')
|
|
445
|
+
this.term.write(`\x1b[32mZMODEM::${type}::START\x1b[0m\r\n\r\n`)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
onReceiveZmodemSession = async () => {
|
|
449
|
+
const savePath = await this.openSaveFolderSelect()
|
|
428
450
|
this.zsession.on('offer', this.onOfferReceive)
|
|
429
451
|
this.zsession.start()
|
|
452
|
+
this.term.write('\r\n\x1b[2A\r\n')
|
|
453
|
+
if (!savePath) {
|
|
454
|
+
return this.onZmodemEnd()
|
|
455
|
+
}
|
|
456
|
+
this.writeBanner('RECEIVE')
|
|
457
|
+
this.zmodemSavePath = savePath
|
|
430
458
|
return new Promise((resolve) => {
|
|
431
459
|
this.zsession.on('session_end', resolve)
|
|
432
|
-
})
|
|
460
|
+
})
|
|
461
|
+
.then(this.onZmodemEnd)
|
|
462
|
+
.catch(this.onZmodemCatch)
|
|
433
463
|
}
|
|
434
464
|
|
|
435
|
-
|
|
436
|
-
if (this.
|
|
465
|
+
initZmodemDownload = async (name, size) => {
|
|
466
|
+
if (!this.zmodemSavePath) {
|
|
437
467
|
return
|
|
438
468
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
if (percent > 99) {
|
|
446
|
-
percent = 99
|
|
469
|
+
let pth = window.pre.resolve(
|
|
470
|
+
this.zmodemSavePath, name
|
|
471
|
+
)
|
|
472
|
+
const exist = await fs.exists(pth)
|
|
473
|
+
if (exist) {
|
|
474
|
+
pth = pth + '.' + generate()
|
|
447
475
|
}
|
|
448
|
-
this.
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
476
|
+
const fd = await fs.open(pth, 'w').catch(this.onZmodemEnd)
|
|
477
|
+
this.downloadFd = fd
|
|
478
|
+
this.downloadPath = pth
|
|
479
|
+
this.downloadCount = 0
|
|
480
|
+
this.zmodemStartTime = Date.now()
|
|
481
|
+
this.downloadSize = size
|
|
482
|
+
this.updateZmodemProgress(
|
|
483
|
+
0, pth, size, transferTypeMap.download
|
|
484
|
+
)
|
|
485
|
+
return fd
|
|
456
486
|
}
|
|
457
487
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
488
|
+
onOfferReceive = async (xfer) => {
|
|
489
|
+
const {
|
|
490
|
+
name,
|
|
491
|
+
size
|
|
492
|
+
} = xfer.get_details()
|
|
493
|
+
if (!this.downloadFd) {
|
|
494
|
+
await this.initZmodemDownload(name, size)
|
|
495
|
+
}
|
|
496
|
+
xfer.on('input', this.onZmodemDownload)
|
|
497
|
+
this.xfer = xfer
|
|
498
|
+
await xfer.accept()
|
|
499
|
+
.then(this.finishZmodemTransfer)
|
|
500
|
+
.catch(this.onZmodemEnd)
|
|
461
501
|
}
|
|
462
502
|
|
|
463
|
-
|
|
464
|
-
this.
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
this.updateProgress(xfer, transferTypeMap.download)
|
|
468
|
-
FILE_BUFFER.push(new Uint8Array(payload))
|
|
469
|
-
})
|
|
470
|
-
xfer.accept()
|
|
471
|
-
.then(
|
|
472
|
-
() => {
|
|
473
|
-
this.saveToDisk(xfer, FILE_BUFFER)
|
|
474
|
-
}
|
|
475
|
-
)
|
|
476
|
-
.catch(window.store.onError)
|
|
503
|
+
checkCache = async () => {
|
|
504
|
+
if (this.DownloadCache?.length > 0) {
|
|
505
|
+
return fs.write(this.downloadFd, new Uint8Array(this.DownloadCache))
|
|
506
|
+
}
|
|
477
507
|
}
|
|
478
508
|
|
|
479
|
-
|
|
480
|
-
if (!
|
|
481
|
-
return
|
|
509
|
+
onZmodemDownload = async payload => {
|
|
510
|
+
if (this.onCanceling || !this.downloadFd) {
|
|
511
|
+
return
|
|
482
512
|
}
|
|
483
|
-
//
|
|
484
|
-
//
|
|
485
|
-
//
|
|
486
|
-
//
|
|
487
|
-
//
|
|
488
|
-
//
|
|
489
|
-
//
|
|
490
|
-
//
|
|
491
|
-
//
|
|
492
|
-
//
|
|
493
|
-
//
|
|
494
|
-
// // return this.transferBySftp(files)
|
|
495
|
-
// // } else {
|
|
496
|
-
// const url = 'https://github.com/FGasper/zmodemjs/issues/11'
|
|
497
|
-
// const msg = (
|
|
498
|
-
// <div>
|
|
499
|
-
// <p>Currently <b>rz</b> only support upload file size less than {filesize(maxZmodemUploadSize)}, due to known issue:</p>
|
|
500
|
-
// <p><Link to={url}>{url}</Link></p>
|
|
501
|
-
// <p>You can try upload in sftp which is much faster.</p>
|
|
502
|
-
// </div>
|
|
513
|
+
// if (!this.DownloadCache) {
|
|
514
|
+
// this.DownloadCache = []
|
|
515
|
+
// }
|
|
516
|
+
// this.DownloadCache = this.DownloadCache.concat(payload)
|
|
517
|
+
// this.downloadCount += payload.length
|
|
518
|
+
// if (this.DownloadCache.length < zmodemTransferPackSize) {
|
|
519
|
+
// return this.updateZmodemProgress(
|
|
520
|
+
// this.downloadCount,
|
|
521
|
+
// this.downloadPath,
|
|
522
|
+
// this.downloadSize,
|
|
523
|
+
// transferTypeMap.download
|
|
503
524
|
// )
|
|
504
|
-
// notification.error({
|
|
505
|
-
// message: msg,
|
|
506
|
-
// duration: 8
|
|
507
|
-
// })
|
|
508
|
-
// // }
|
|
509
525
|
// }
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
on_progress (obj, xfer) {
|
|
520
|
-
th.updateProgress(xfer, transferTypeMap.upload)
|
|
521
|
-
}
|
|
522
|
-
}
|
|
526
|
+
// this.writeCache = this.DownloadCache
|
|
527
|
+
// this.DownloadCache = []
|
|
528
|
+
this.downloadCount += payload.length
|
|
529
|
+
await fs.write(this.downloadFd, new Uint8Array(payload))
|
|
530
|
+
this.updateZmodemProgress(
|
|
531
|
+
this.downloadCount,
|
|
532
|
+
this.downloadPath,
|
|
533
|
+
this.downloadSize,
|
|
534
|
+
transferTypeMap.download
|
|
523
535
|
)
|
|
524
|
-
|
|
525
|
-
.catch(th.onZmodemCatch)
|
|
536
|
+
}
|
|
526
537
|
|
|
527
|
-
|
|
538
|
+
updateZmodemProgress = throttle((start, name, size, type) => {
|
|
539
|
+
this.zmodemTransfer = {
|
|
540
|
+
type,
|
|
541
|
+
start,
|
|
542
|
+
name,
|
|
543
|
+
size
|
|
544
|
+
}
|
|
545
|
+
this.writeZmodemProgress()
|
|
546
|
+
}, 500)
|
|
547
|
+
|
|
548
|
+
finishZmodemTransfer = () => {
|
|
549
|
+
this.zmodemTransfer = {
|
|
550
|
+
...this.zmodemTransfer,
|
|
551
|
+
start: this.zmodemTransfer.size
|
|
552
|
+
}
|
|
553
|
+
this.writeZmodemProgress()
|
|
528
554
|
}
|
|
529
555
|
|
|
530
|
-
|
|
531
|
-
this.
|
|
532
|
-
return
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
556
|
+
writeZmodemProgress = () => {
|
|
557
|
+
if (this.onCanceling) {
|
|
558
|
+
return
|
|
559
|
+
}
|
|
560
|
+
const {
|
|
561
|
+
size, start, name
|
|
562
|
+
} = this.zmodemTransfer
|
|
563
|
+
const speed = size > 0 ? formatBytes(start * 1000 / 1024 / (Date.now() - this.zmodemStartTime)) : 0
|
|
564
|
+
const percent = size > 0 ? Math.floor(start * 100 / size) : 100
|
|
565
|
+
const str = `\x1b[32m${name}\x1b[0m::${percent}%,${start}/${size},${speed}/s`
|
|
566
|
+
this.term.write('\r\n\x1b[2A' + str + '\n')
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
zmodemTransferFile = async (file, filesRemaining, sizeRemaining) => {
|
|
570
|
+
const offer = {
|
|
571
|
+
obj: file,
|
|
572
|
+
name: file.name,
|
|
573
|
+
size: file.size,
|
|
574
|
+
files_remaining: filesRemaining,
|
|
575
|
+
bytes_remaining: sizeRemaining
|
|
576
|
+
}
|
|
577
|
+
const xfer = await this.zsession.send_offer(offer)
|
|
578
|
+
if (!xfer) {
|
|
579
|
+
this.onZmodemEnd()
|
|
580
|
+
return window.store.onError(new Error('Transfer cancelled, maybe file already exists'))
|
|
581
|
+
}
|
|
582
|
+
this.zmodemStartTime = Date.now()
|
|
583
|
+
const fd = await fs.open(file.filePath, 'r')
|
|
584
|
+
let start = 0
|
|
585
|
+
const { size } = file
|
|
586
|
+
let inited = false
|
|
587
|
+
while (start < size || !inited) {
|
|
588
|
+
const rest = size - start
|
|
589
|
+
const len = rest > zmodemTransferPackSize ? zmodemTransferPackSize : rest
|
|
590
|
+
const buffer = new Uint8Array(len)
|
|
591
|
+
const newArr = await fs.read(fd, buffer, 0, len, null)
|
|
592
|
+
const n = newArr.length
|
|
593
|
+
await xfer.send(newArr)
|
|
594
|
+
start = start + n
|
|
595
|
+
inited = true
|
|
596
|
+
this.updateZmodemProgress(start, file.name, size, transferTypeMap.upload)
|
|
597
|
+
if (n < zmodemTransferPackSize || start >= file.size || this.onCanceling) {
|
|
598
|
+
break
|
|
536
599
|
}
|
|
600
|
+
}
|
|
601
|
+
await fs.close(fd)
|
|
602
|
+
this.finishZmodemTransfer()
|
|
603
|
+
await xfer.end()
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
openFileSelect = async () => {
|
|
607
|
+
const properties = [
|
|
608
|
+
'openFile',
|
|
609
|
+
'multiSelections',
|
|
610
|
+
'showHiddenFiles',
|
|
611
|
+
'noResolveAliases',
|
|
612
|
+
'treatPackageAsDirectory',
|
|
613
|
+
'dontAddToRecent'
|
|
614
|
+
]
|
|
615
|
+
const files = await window.api.openDialog({
|
|
616
|
+
title: 'Choose some files to send',
|
|
617
|
+
message: 'Choose some files to send',
|
|
618
|
+
properties
|
|
537
619
|
})
|
|
620
|
+
if (!files || !files.length) {
|
|
621
|
+
return this.onZmodemEnd()
|
|
622
|
+
}
|
|
623
|
+
const r = []
|
|
624
|
+
for (const filePath of files) {
|
|
625
|
+
const stat = await getLocalFileInfo(filePath)
|
|
626
|
+
r.push({ ...stat, filePath })
|
|
627
|
+
}
|
|
628
|
+
return r
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
openSaveFolderSelect = async () => {
|
|
632
|
+
const savePaths = await window.api.openDialog({
|
|
633
|
+
title: 'Choose a folder to save file(s)',
|
|
634
|
+
message: 'Choose a folder to save file(s)',
|
|
635
|
+
properties: [
|
|
636
|
+
'openDirectory',
|
|
637
|
+
'showHiddenFiles',
|
|
638
|
+
'createDirectory',
|
|
639
|
+
'noResolveAliases',
|
|
640
|
+
'treatPackageAsDirectory',
|
|
641
|
+
'dontAddToRecent'
|
|
642
|
+
]
|
|
643
|
+
})
|
|
644
|
+
if (!savePaths || !savePaths.length) {
|
|
645
|
+
return false
|
|
646
|
+
}
|
|
647
|
+
return savePaths[0]
|
|
538
648
|
}
|
|
539
649
|
|
|
540
|
-
|
|
541
|
-
|
|
650
|
+
beforeZmodemUpload = async (files) => {
|
|
651
|
+
if (!files || !files.length) {
|
|
652
|
+
return false
|
|
653
|
+
}
|
|
654
|
+
this.writeBanner('SEND')
|
|
655
|
+
let filesRemaining = files.length
|
|
656
|
+
let sizeRemaining = files.reduce((a, b) => a + b.size, 0)
|
|
657
|
+
for (const f of files) {
|
|
658
|
+
await this.zmodemTransferFile(f, filesRemaining, sizeRemaining)
|
|
659
|
+
filesRemaining = filesRemaining - 1
|
|
660
|
+
sizeRemaining = sizeRemaining - f.size
|
|
661
|
+
}
|
|
662
|
+
this.onZmodemEnd()
|
|
542
663
|
}
|
|
543
664
|
|
|
544
|
-
|
|
545
|
-
this.
|
|
546
|
-
this.
|
|
665
|
+
onSendZmodemSession = async () => {
|
|
666
|
+
this.term.write('\r\n\x1b[2A\n')
|
|
667
|
+
const files = await this.openFileSelect()
|
|
668
|
+
this.beforeZmodemUpload(files)
|
|
547
669
|
}
|
|
548
670
|
|
|
549
|
-
onZmodemEnd = () => {
|
|
550
|
-
delete this.
|
|
671
|
+
onZmodemEnd = async () => {
|
|
672
|
+
delete this.zmodemSavePath
|
|
551
673
|
this.onCanceling = true
|
|
552
|
-
this.
|
|
553
|
-
this.
|
|
554
|
-
undefined,
|
|
555
|
-
this.props.tab.encode,
|
|
556
|
-
isWin && !this.isRemote()
|
|
557
|
-
)
|
|
558
|
-
if (this.decoder) {
|
|
559
|
-
this.attachAddon.decoder = this.decode
|
|
674
|
+
if (this.downloadFd) {
|
|
675
|
+
await fs.close(this.downloadFd)
|
|
560
676
|
}
|
|
561
|
-
this.
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
677
|
+
if (this.xfer && this.xfer.end) {
|
|
678
|
+
await this.xfer.end().catch(
|
|
679
|
+
console.error
|
|
680
|
+
)
|
|
681
|
+
}
|
|
682
|
+
delete this.xfer
|
|
683
|
+
if (this.zsession && this.zsession.close) {
|
|
684
|
+
await this.zsession.close().catch(
|
|
685
|
+
console.error
|
|
686
|
+
)
|
|
687
|
+
}
|
|
688
|
+
delete this.zsession
|
|
567
689
|
this.term.focus()
|
|
568
690
|
this.term.write('\r\n')
|
|
691
|
+
delete this.downloadFd
|
|
692
|
+
delete this.downloadPath
|
|
693
|
+
delete this.downloadCount
|
|
694
|
+
delete this.downloadSize
|
|
695
|
+
delete this.DownloadCache
|
|
569
696
|
}
|
|
570
697
|
|
|
571
698
|
onZmodemCatch = (e) => {
|
|
@@ -575,7 +702,6 @@ class Term extends Component {
|
|
|
575
702
|
|
|
576
703
|
onZmodemDetect = detection => {
|
|
577
704
|
this.onCanceling = false
|
|
578
|
-
this.attachAddon.dispose()
|
|
579
705
|
this.term.blur()
|
|
580
706
|
this.onZmodem = true
|
|
581
707
|
const zsession = detection.confirm()
|
|
@@ -1055,9 +1181,7 @@ class Term extends Component {
|
|
|
1055
1181
|
this.zmodemAddon = new AddonZmodem()
|
|
1056
1182
|
this.fitAddon.fit()
|
|
1057
1183
|
term.loadAddon(this.zmodemAddon)
|
|
1058
|
-
term.zmodemAttach(this
|
|
1059
|
-
noTerminalWriteOutsideSession: true
|
|
1060
|
-
}, this)
|
|
1184
|
+
term.zmodemAttach(this)
|
|
1061
1185
|
}
|
|
1062
1186
|
term.displayRaw = displayRaw
|
|
1063
1187
|
term.loadAddon(
|
|
@@ -1085,7 +1209,7 @@ class Term extends Component {
|
|
|
1085
1209
|
|
|
1086
1210
|
onerrorSocket = err => {
|
|
1087
1211
|
this.setStatus(statusMap.error)
|
|
1088
|
-
log.
|
|
1212
|
+
log.error('onerrorSocket', err)
|
|
1089
1213
|
}
|
|
1090
1214
|
|
|
1091
1215
|
oncloseSocket = () => {
|
|
@@ -1184,7 +1308,7 @@ class Term extends Component {
|
|
|
1184
1308
|
}
|
|
1185
1309
|
|
|
1186
1310
|
render () {
|
|
1187
|
-
const { id, loading
|
|
1311
|
+
const { id, loading } = this.state
|
|
1188
1312
|
const { height, width, left, top, position, id: pid, activeSplitId } = this.props
|
|
1189
1313
|
const cls = classnames('term-wrap', {
|
|
1190
1314
|
'not-first-term': !!position
|
|
@@ -1242,11 +1366,6 @@ class Term extends Component {
|
|
|
1242
1366
|
close={this.closeNormalBuffer}
|
|
1243
1367
|
/>
|
|
1244
1368
|
</div>
|
|
1245
|
-
<ZmodemTransfer
|
|
1246
|
-
zmodemTransfer={zmodemTransfer}
|
|
1247
|
-
cancelZmodem={this.cancelZmodem}
|
|
1248
|
-
beforeZmodemUpload={this.beforeZmodemUpload}
|
|
1249
|
-
/>
|
|
1250
1369
|
<Spin className='loading-wrapper' spinning={loading} />
|
|
1251
1370
|
</div>
|
|
1252
1371
|
)
|
|
@@ -1,48 +1,55 @@
|
|
|
1
1
|
import zmodem from 'zmodem.js/src/zmodem_browser'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
function shouldWrite () {
|
|
9
|
-
return !!zsentry.get_confirmed_session() || !opts.noTerminalWriteOutsideSession
|
|
3
|
+
export class AddonZmodem {
|
|
4
|
+
_disposables = []
|
|
5
|
+
|
|
6
|
+
activate (terminal) {
|
|
7
|
+
terminal.zmodemAttach = this.zmodemAttach.bind(this)
|
|
10
8
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
},
|
|
17
|
-
sender: senderFunc,
|
|
18
|
-
on_retract: ctx.onzmodemRetract,
|
|
19
|
-
on_detect: ctx.onZmodemDetect
|
|
20
|
-
})
|
|
21
|
-
function handleWSMessage (evt) {
|
|
22
|
-
if (typeof evt.data === 'string') {
|
|
23
|
-
if (shouldWrite()) {
|
|
24
|
-
term.write(evt.data)
|
|
25
|
-
}
|
|
9
|
+
|
|
10
|
+
sendWebSocket (octets) {
|
|
11
|
+
const { socket } = this
|
|
12
|
+
if (socket && socket.readyState === WebSocket.OPEN) {
|
|
13
|
+
return socket.send(new Uint8Array(octets))
|
|
26
14
|
} else {
|
|
27
|
-
|
|
15
|
+
console.error('WebSocket is not open')
|
|
28
16
|
}
|
|
29
17
|
}
|
|
30
|
-
ws.binaryType = 'arraybuffer'
|
|
31
|
-
ws.addEventListener('message', handleWSMessage)
|
|
32
|
-
}
|
|
33
18
|
|
|
34
|
-
|
|
35
|
-
|
|
19
|
+
zmodemAttach (ctx) {
|
|
20
|
+
this.socket = ctx.socket
|
|
21
|
+
this.term = ctx.term
|
|
22
|
+
this.ctx = ctx
|
|
23
|
+
this.zsentry = new zmodem.Sentry({
|
|
24
|
+
to_terminal: (octets) => {
|
|
25
|
+
if (ctx.onZmodem) {
|
|
26
|
+
this.term.write(String.fromCharCode.apply(String, octets))
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
sender: this.sendWebSocket.bind(this),
|
|
30
|
+
on_retract: ctx.onzmodemRetract,
|
|
31
|
+
on_detect: ctx.onZmodemDetect
|
|
32
|
+
})
|
|
33
|
+
this.socket.binaryType = 'arraybuffer'
|
|
34
|
+
this.socket.addEventListener('message', this.handleWSMessage.bind(this))
|
|
35
|
+
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
handleWSMessage (evt) {
|
|
38
|
+
if (typeof evt.data === 'string') {
|
|
39
|
+
if (this.ctx.onZmodem) {
|
|
40
|
+
this.term.write(evt.data)
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
this.zsentry.consume(evt.data)
|
|
44
|
+
}
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
dispose () {
|
|
48
|
+
this.socket && this.socket.removeEventListener('message', this.handleWSMessage)
|
|
43
49
|
this._disposables.forEach(d => d.dispose())
|
|
44
50
|
this._disposables.length = 0
|
|
51
|
+
this.term = null
|
|
52
|
+
this.zsentry = null
|
|
53
|
+
this.socket = null
|
|
45
54
|
}
|
|
46
55
|
}
|
|
47
|
-
|
|
48
|
-
export const Zmodem = zmodem
|
package/client/css/basic.styl
CHANGED
package/client/entry/basic.js
CHANGED
package/client/store/index.js
CHANGED
|
@@ -216,7 +216,6 @@ class Store {
|
|
|
216
216
|
store
|
|
217
217
|
} = window
|
|
218
218
|
return store.showModal ||
|
|
219
|
-
store.termSearchOpen ||
|
|
220
219
|
store.showInfoModal ||
|
|
221
220
|
store.showEditor ||
|
|
222
221
|
store.showFileModal
|
|
@@ -226,10 +225,6 @@ class Store {
|
|
|
226
225
|
return 0
|
|
227
226
|
}
|
|
228
227
|
|
|
229
|
-
get tabsHeight () {
|
|
230
|
-
return 45 // window.store.config.useSystemTitleBar ? 45 : 56
|
|
231
|
-
}
|
|
232
|
-
|
|
233
228
|
get langs () {
|
|
234
229
|
return JSON.parse(window.store._langs)
|
|
235
230
|
}
|