@electerm/electerm-react 1.80.5 → 1.80.18

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.
@@ -8,7 +8,7 @@ export default {
8
8
  scrollback: 3000,
9
9
  onStartSessions: [],
10
10
  fontSize: 16,
11
- fontFamily: 'Fira Code, mono, courier-new, courier, monospace',
11
+ fontFamily: 'Maple Mono, mono, courier-new, courier, monospace',
12
12
  execWindows: 'System32/WindowsPowerShell/v1.0/powershell.exe',
13
13
  execMac: 'zsh',
14
14
  execLinux: 'bash',
@@ -153,7 +153,7 @@ export default class ConfirmModalStore extends Component {
153
153
  <Button
154
154
  type='dashed'
155
155
  className='mg1l'
156
- onClick={() => this.act(fileActions.cancel)}
156
+ onClick={() => this.act(fileActions.skipAll)}
157
157
  >
158
158
  {e('cancel')}
159
159
  </Button>
@@ -208,6 +208,16 @@ export default class ConfirmModalStore extends Component {
208
208
  >
209
209
  {e('renameAll')}
210
210
  </Button>
211
+ <Button
212
+ type='primary'
213
+ className='mg1l'
214
+ title={e('skipAll')}
215
+ onClick={
216
+ () => this.act(fileActions.skipAll)
217
+ }
218
+ >
219
+ {e('skipAll')}
220
+ </Button>
211
221
  </div>
212
222
  </div>
213
223
  )
@@ -28,6 +28,7 @@ export default class TransportAction extends Component {
28
28
  refsTransfers.add(this.id, this)
29
29
  this.total = 0
30
30
  this.transferred = 0
31
+ this.currentProgress = 1
31
32
  }
32
33
 
33
34
  componentDidMount () {
@@ -421,11 +422,11 @@ export default class TransportAction extends Component {
421
422
  }
422
423
  this.transferred += transferred
423
424
  const up = {}
424
- let percent = this.total === 0
425
- ? 0
426
- : Math.floor(100 * this.transferred / this.total)
427
- percent = percent >= 100 ? 99 : percent
428
- up.percent = percent
425
+
426
+ // Increment progress slightly with each file/folder (but never exceed 99%)
427
+ this.currentProgress = Math.min(this.currentProgress + 0.2, 99)
428
+
429
+ up.percent = Math.floor(this.currentProgress)
429
430
  up.status = 'active'
430
431
  up.transferred = this.transferred
431
432
  up.startTime = this.startTime
@@ -505,49 +506,157 @@ export default class TransportAction extends Component {
505
506
  return transfer
506
507
  }
507
508
 
508
- transferFolderRecursive = async (transfer = this.getDefaultTransfer()) => {
509
+ // Handle file transfers in parallel batches
510
+ transferFiles = async (files, batch, transfer) => {
511
+ if (this.onCancel) {
512
+ return
513
+ }
514
+
515
+ const { fromPath, toPath } = transfer
516
+
517
+ // Process files in batches
518
+ for (let i = 0; i < files.length; i += batch) {
519
+ if (this.onCancel) {
520
+ return
521
+ }
522
+
523
+ const batchFiles = files.slice(i, i + batch)
524
+ const promises = batchFiles.map(file => {
525
+ if (this.onCancel) {
526
+ return Promise.resolve(0)
527
+ }
528
+
529
+ const fromItemPath = resolve(fromPath, file.name)
530
+ const toItemPath = resolve(toPath, file.name)
531
+
532
+ const itemTransfer = {
533
+ ...transfer,
534
+ fromPath: fromItemPath,
535
+ toPath: toItemPath,
536
+ fromFile: file
537
+ }
538
+
539
+ return this.transferFileAsSubTransfer(itemTransfer)
540
+ })
541
+
542
+ // Wait for all files in batch to complete
543
+ const results = await Promise.all(promises)
544
+
545
+ // Update progress once for the entire batch
546
+ const batchTotalSize = results.reduce((sum, size) => sum + size, 0)
547
+ if (batchTotalSize > 0) {
548
+ this.onFolderData(batchTotalSize)
549
+ }
550
+ }
551
+ }
552
+
553
+ // Handle folder transfers sequentially to prevent concurrency explosion
554
+ transferFolders = async (folders, batch, transfer) => {
555
+ if (this.onCancel) {
556
+ return
557
+ }
558
+
559
+ const { fromPath, toPath } = transfer
560
+
561
+ // Step 1: Create all folders concurrently in batches
562
+ for (let i = 0; i < folders.length; i += batch) {
563
+ if (this.onCancel) {
564
+ return
565
+ }
566
+
567
+ const batchFolders = folders.slice(i, i + batch)
568
+ const createFolderPromises = batchFolders.map(folder => {
569
+ const toItemPath = resolve(toPath, folder.name)
570
+
571
+ // Create folder itself (don't process contents)
572
+ const createTransfer = {
573
+ ...transfer,
574
+ toPath: toItemPath,
575
+ fromFile: folder
576
+ }
577
+
578
+ return this.mkdir(createTransfer)
579
+ })
580
+
581
+ // Create all folders in this batch concurrently
582
+ await Promise.all(createFolderPromises)
583
+ }
584
+
585
+ // Step 2: Process contents of each folder sequentially
586
+ for (const folder of folders) {
587
+ if (this.onCancel) {
588
+ return
589
+ }
590
+
591
+ const fromItemPath = resolve(fromPath, folder.name)
592
+ const toItemPath = resolve(toPath, folder.name)
593
+
594
+ const itemTransfer = {
595
+ ...transfer,
596
+ fromPath: fromItemPath,
597
+ toPath: toItemPath,
598
+ fromFile: folder
599
+ }
600
+
601
+ // Transfer folder contents (set createFolder = false since we already created it)
602
+ await this.transferFolderRecursive(itemTransfer, false)
603
+ }
604
+ }
605
+
606
+ // Main recursive function using the separate handlers
607
+ transferFolderRecursive = async (transfer = this.getDefaultTransfer(), createFolder = true) => {
509
608
  if (this.onCancel) {
510
609
  return
511
610
  }
512
611
  const {
513
612
  fromPath,
514
- toPath,
515
613
  typeFrom,
516
- typeTo,
517
614
  sessionId,
518
615
  toFile,
519
616
  isRenamed
520
617
  } = transfer
521
- if (!toFile || isRenamed) {
618
+
619
+ if (createFolder && (!toFile || isRenamed)) {
522
620
  const folderCreated = await this.mkdir(transfer)
523
621
  if (!folderCreated) {
524
622
  return
525
623
  }
526
624
  }
625
+
527
626
  const list = await this.list(typeFrom, fromPath, sessionId)
627
+ const bigFileSize = 1024 * 1024
628
+ const smallFilesBatch = 30
629
+ const BigFilesBatch = 3
630
+ const foldersBatch = 50
528
631
 
529
- for (const item of list) {
530
- if (!item.isDirectory) {
531
- this.total += item.size
632
+ const {
633
+ folders,
634
+ smallFiles,
635
+ largeFiles
636
+ } = list.reduce((p, c) => {
637
+ if (c.isDirectory) {
638
+ p.folders.push(c)
639
+ } else {
640
+ this.total += c.size
641
+ if (c.size < bigFileSize) {
642
+ p.smallFiles.push(c)
643
+ } else {
644
+ p.largeFiles.push(c)
645
+ }
532
646
  }
533
- const fromItemPath = resolve(fromPath, item.name)
534
- const toItemPath = resolve(toPath, item.name)
647
+ return p
648
+ }, {
649
+ folders: [],
650
+ smallFiles: [],
651
+ largeFiles: []
652
+ })
535
653
 
536
- const itemTransfer = {
537
- ...transfer,
538
- fromPath: fromItemPath,
539
- toPath: toItemPath,
540
- fromFile: item
541
- }
654
+ // Process files with parallel batching
655
+ await this.transferFiles(smallFiles, smallFilesBatch, transfer)
656
+ await this.transferFiles(largeFiles, BigFilesBatch, transfer)
542
657
 
543
- const toFile = await this.checkExist(typeTo, toItemPath, sessionId)
544
- itemTransfer.toFile = toFile
545
- if (item.isDirectory) {
546
- await this.transferFolderRecursive(itemTransfer)
547
- } else {
548
- await this.transferFileAsSubTransfer(itemTransfer)
549
- }
550
- }
658
+ // Process folders sequentially
659
+ await this.transferFolders(folders, foldersBatch, transfer)
551
660
  }
552
661
 
553
662
  onError = (e) => {
@@ -34,6 +34,9 @@ export default class Upgrade extends PureComponent {
34
34
  downloadTimer = null
35
35
 
36
36
  componentDidMount () {
37
+ if (window.et.isWebApp) {
38
+ return
39
+ }
37
40
  setTimeout(() => {
38
41
  getLatestReleaseVersion(1)
39
42
  }, 5000)
@@ -14,7 +14,7 @@ export default function FileIcon ({ file, ...extra }) {
14
14
  return (
15
15
  <img
16
16
  src={extIconPath + name}
17
- height={12}
17
+ height={16}
18
18
  alt=''
19
19
  {...extra}
20
20
  />
@@ -671,7 +671,7 @@ export default class FileSection extends React.Component {
671
671
  const {
672
672
  path, name
673
673
  } = this.state.file
674
- const rp = resolve(path, name)
674
+ const rp = path ? resolve(path, name) : this.props[`${this.props.type}Path`]
675
675
  this.props.tab.pane = paneMap.terminal
676
676
  refs.get('term-' + this.props.tab.id)?.cd(rp)
677
677
  }
@@ -953,7 +953,7 @@ export default class FileSection extends React.Component {
953
953
  })
954
954
  }
955
955
  if (
956
- isDirectory && isRealFile &&
956
+ isDirectory &&
957
957
  (
958
958
  (hasHost && enableSsh !== false && isRemote) ||
959
959
  (isLocal && !hasHost)
@@ -49,7 +49,7 @@ export default class FileListTableHeader extends Component {
49
49
  }
50
50
  const text = e(id || '')
51
51
  const directionIcon = isSorting
52
- ? (sortDirection === 'asc' ? <DownOutlined /> : <UpOutlined />)
52
+ ? (sortDirection === 'asc' ? <UpOutlined /> : <DownOutlined />)
53
53
  : null
54
54
  const itemProps = {
55
55
  onClick: this.props.onClickName,
@@ -1,15 +1,35 @@
1
- /**
2
- * bookmark select
3
- */
4
-
1
+ import { refsStatic } from '../common/ref'
2
+ import { useEffect, useRef } from 'react'
5
3
  import BookmarkSelect from './bookmark-select'
4
+ import { debounce } from 'lodash-es'
6
5
 
7
6
  export default function BookmarkPanel (props) {
8
7
  const { store } = window
8
+ const bookmarksPanelRef = useRef(null)
9
+ const SCROLL_REF_ID = 'bookmarks-scroll-position'
10
+
11
+ // On component mount, restore scroll position
12
+ useEffect(() => {
13
+ if (store.openedSideBar) {
14
+ const savedPosition = refsStatic.get(SCROLL_REF_ID)
15
+ if (savedPosition) {
16
+ setTimeout(() => {
17
+ bookmarksPanelRef.current.scrollTop = savedPosition
18
+ }, 100)
19
+ }
20
+ }
21
+ }, [store.openedSideBar])
22
+
23
+ // Save scroll position when scrolling
24
+ const handleScroll = debounce((e) => {
25
+ const top = e.target.scrollTop
26
+ if (top > 0) {
27
+ refsStatic.add(SCROLL_REF_ID, e.target.scrollTop)
28
+ }
29
+ }, 100)
30
+
9
31
  return (
10
- <div
11
- className='sidebar-panel-bookmarks'
12
- >
32
+ <div className='sidebar-panel-bookmarks' ref={bookmarksPanelRef} onScroll={handleScroll}>
13
33
  <div className='pd2l sidebar-inner'>
14
34
  <BookmarkSelect store={store} from='sidebar' />
15
35
  </div>
@@ -32,7 +32,7 @@ export default auto(function InfoModal (props) {
32
32
  }
33
33
 
34
34
  const renderCheckUpdate = () => {
35
- if (srcsSkipUpgradeCheck.includes(props.installSrc)) {
35
+ if (window.et.isWebApp || srcsSkipUpgradeCheck.includes(props.installSrc)) {
36
36
  return null
37
37
  }
38
38
  const {
@@ -40,6 +40,8 @@
40
40
  top 112px
41
41
  bottom 0
42
42
  overflow-y scroll
43
+ .item-list-wrap
44
+ overflow-y hidden
43
45
  .not-system-ui.is-mac
44
46
  .sidebar-bar
45
47
  margin-top 20px
@@ -53,11 +53,14 @@ export default class TermSearch extends PureComponent {
53
53
  }
54
54
 
55
55
  toggleSearch = () => {
56
- if (this.props.termSearchOpen) {
56
+ const isClosing = this.props.termSearchOpen
57
+ if (isClosing) {
57
58
  this.clearSearch()
58
59
  }
59
60
  window.store.toggleTerminalSearch()
60
- setTimeout(window.store.focus, 200)
61
+ if (isClosing) {
62
+ setTimeout(window.store.focus, 200)
63
+ }
61
64
  }
62
65
 
63
66
  prev = (v = this.props.termSearch) => {
@@ -336,6 +336,7 @@ clear\r`
336
336
  try {
337
337
  const fileData = JSON.parse(fromFile)
338
338
  const filePath = resolve(fileData.path, fileData.name)
339
+ console.log('filePath', filePath)
339
340
  if (this.isUnsafeFilename(filePath)) {
340
341
  message.error(notSafeMsg)
341
342
  return
@@ -350,14 +351,15 @@ clear\r`
350
351
  // Handle regular file drop
351
352
  const files = dt.files
352
353
  if (files && files.length) {
353
- const filesAll = Array.from(files).map(f => `"${f.path}"`).join(' ')
354
- if (this.isUnsafeFilename(filesAll)) {
354
+ const arr = Array.from(files)
355
+ // Check each file path individually
356
+ const hasUnsafeFilename = arr.some(f => this.isUnsafeFilename(f.path))
357
+ if (hasUnsafeFilename) {
355
358
  message.error(notSafeMsg)
356
359
  return
357
360
  }
358
- this.attachAddon._sendData(
359
- Array.from(files).map(f => `"${f.path}"`).join(' ')
360
- )
361
+ const filesAll = arr.map(f => `"${f.path}"`).join(' ')
362
+ this.attachAddon._sendData(filesAll)
361
363
  }
362
364
  }
363
365
 
@@ -2,7 +2,7 @@ import { createRoot } from 'react-dom/client'
2
2
  import 'antd/dist/reset.css'
3
3
  import '@xterm/xterm/css/xterm.css'
4
4
  import '../common/trzsz.js'
5
- import 'firacode/distr/fira_code.css'
5
+ import '@fontsource/maple-mono/index.css'
6
6
  import Main from '../components/main/index.jsx'
7
7
 
8
8
  const rootElement = createRoot(document.getElementById('container'))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "1.80.5",
3
+ "version": "1.80.18",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",