@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.
Files changed (38) hide show
  1. package/client/common/constants.js +2 -1
  2. package/client/common/default-setting.js +1 -1
  3. package/client/common/pre.js +1 -1
  4. package/client/components/batch-op/batch-op.jsx +7 -1
  5. package/client/components/common/logo-elem.jsx +4 -3
  6. package/client/components/common/logo.styl +9 -0
  7. package/client/components/footer/footer-entry.jsx +1 -1
  8. package/client/components/footer/footer.styl +11 -2
  9. package/client/components/main/main.jsx +1 -0
  10. package/client/components/main/wrapper.styl +0 -2
  11. package/client/components/quick-commands/qm.styl +3 -0
  12. package/client/components/quick-commands/quick-commands-box.jsx +1 -1
  13. package/client/components/quick-commands/quick-commands-select.jsx +3 -1
  14. package/client/components/session/session.jsx +7 -1
  15. package/client/components/session/sessions.jsx +7 -0
  16. package/client/components/setting-panel/setting-common.jsx +5 -5
  17. package/client/components/setting-panel/setting-modal.jsx +3 -1
  18. package/client/components/setting-panel/setting-wrap.jsx +10 -0
  19. package/client/components/setting-panel/setting-wrap.styl +28 -1
  20. package/client/components/setting-panel/setting.styl +8 -1
  21. package/client/components/shortcuts/shortcut-handler.js +11 -0
  22. package/client/components/shortcuts/shortcuts-defaults.js +5 -0
  23. package/client/components/sidebar/sidebar.styl +4 -1
  24. package/client/components/tabs/index.jsx +9 -2
  25. package/client/components/tabs/tab.jsx +17 -6
  26. package/client/components/tabs/tabs.styl +13 -6
  27. package/client/components/tabs/window-control.jsx +4 -1
  28. package/client/components/terminal/attach-addon-custom.js +11 -2
  29. package/client/components/terminal/fs.js +59 -0
  30. package/client/components/terminal/index.jsx +245 -126
  31. package/client/components/terminal/xterm-zmodem.js +40 -33
  32. package/client/css/basic.styl +1 -1
  33. package/client/css/mobile.styl +8 -0
  34. package/client/entry/basic.js +1 -0
  35. package/client/store/index.js +0 -5
  36. package/client/store/init-state.js +2 -1
  37. package/package.json +1 -1
  38. 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 { Zmodem, AddonZmodem } from './xterm-zmodem'
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
- onReceiveZmodemSession = () => {
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
- }).then(this.onZmodemEnd).catch(this.onZmodemCatch)
460
+ })
461
+ .then(this.onZmodemEnd)
462
+ .catch(this.onZmodemCatch)
433
463
  }
434
464
 
435
- updateProgress = (xfer, type) => {
436
- if (this.onCanceling) {
465
+ initZmodemDownload = async (name, size) => {
466
+ if (!this.zmodemSavePath) {
437
467
  return
438
468
  }
439
- const fileInfo = xfer.get_details()
440
- const {
441
- size
442
- } = fileInfo
443
- const total = xfer.get_offset() || 0
444
- let percent = Math.floor(100 * total / size)
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.setState({
449
- zmodemTransfer: {
450
- fileInfo,
451
- percent,
452
- transferedSize: total,
453
- type
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
- saveToDisk = (xfer, buffer) => {
459
- return Zmodem.Browser
460
- .save_to_disk(buffer, xfer.get_details().name)
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
- onOfferReceive = xfer => {
464
- this.updateProgress(xfer, transferTypeMap.download)
465
- const FILE_BUFFER = []
466
- xfer.on('input', (payload) => {
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
- beforeZmodemUpload = (file, files) => {
480
- if (!files.length) {
481
- return false
509
+ onZmodemDownload = async payload => {
510
+ if (this.onCanceling || !this.downloadFd) {
511
+ return
482
512
  }
483
- // const f = files[0]
484
- // if (f.size > maxZmodemUploadSize) {
485
- // if (this.zsession) {
486
- // this.zsession.abort()
487
- // }
488
- // this.onZmodemEnd()
489
- // // if (this.props.tab.enableSftp) {
490
- // // notification.info({
491
- // // message: `Uploading by sftp`,
492
- // // duration: 8
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
- const th = this
511
- Zmodem.Browser.send_files(
512
- this.zsession,
513
- files, {
514
- on_offer_response (obj, xfer) {
515
- if (xfer) {
516
- th.updateProgress(xfer, transferTypeMap.upload)
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
- .then(th.onZmodemEndSend)
525
- .catch(th.onZmodemCatch)
536
+ }
526
537
 
527
- return false
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
- onSendZmodemSession = () => {
531
- this.setState(() => {
532
- return {
533
- zmodemTransfer: {
534
- type: transferTypeMap.upload
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
- cancelZmodem = () => {
541
- this.onZmodemEndSend()
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
- onZmodemEndSend = () => {
545
- this.zsession && this.zsession.close && this.zsession.close()
546
- this.onZmodemEnd()
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.onZmodem
671
+ onZmodemEnd = async () => {
672
+ delete this.zmodemSavePath
551
673
  this.onCanceling = true
552
- this.attachAddon = new AttachAddon(
553
- this.socket,
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.term.loadAddon(this.attachAddon)
562
- this.setState(() => {
563
- return {
564
- zmodemTransfer: null
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.socket, {
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.warning('onerrorSocket', err)
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, zmodemTransfer } = this.state
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
- function zmodemAttach (ws, opts, ctx) {
4
- if (opts === undefined) { opts = {} }
5
- const term = this
6
- const senderFunc = function (octets) { return ws.send(new Uint8Array(octets)) }
7
- let zsentry = null
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
- zsentry = new zmodem.Sentry({
12
- to_terminal: function (octets) {
13
- if (shouldWrite()) {
14
- term.write(String.fromCharCode.apply(String, octets))
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
- zsentry.consume(evt.data)
15
+ console.error('WebSocket is not open')
28
16
  }
29
17
  }
30
- ws.binaryType = 'arraybuffer'
31
- ws.addEventListener('message', handleWSMessage)
32
- }
33
18
 
34
- export class AddonZmodem {
35
- _disposables = []
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
- activate (terminal) {
38
- terminal.zmodemAttach = zmodemAttach
39
- terminal.zmodemBrowser = zmodem.Browser
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
@@ -38,4 +38,4 @@ body
38
38
  display: none !important
39
39
 
40
40
  a
41
- color primary
41
+ color primary
@@ -0,0 +1,8 @@
1
+ .l500
2
+ display none !important
3
+
4
+ @media (max-width: 500px)
5
+ .w500
6
+ display none !important
7
+ .l500
8
+ display inline !important
@@ -2,6 +2,7 @@
2
2
  * init app data then write main script to html body
3
3
  */
4
4
  import '../css/basic.styl'
5
+ import '../css/mobile.styl'
5
6
  import { get as _get } from 'lodash-es'
6
7
  import '../common/pre'
7
8
 
@@ -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
  }