@electerm/electerm-react 3.15.178 → 3.15.187
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/bookmark-schemas.js +2 -0
- package/client/common/default-setting.js +2 -1
- package/client/common/drop-position.js +59 -0
- package/client/components/bookmark-form/bookmark-schema.js +2 -0
- package/client/components/bookmark-form/common/connection-hopping.jsx +33 -7
- package/client/components/bookmark-form/common/ssh-tunnel-form.jsx +25 -7
- package/client/components/bookmark-form/common/ssh-tunnels.jsx +1 -1
- package/client/components/bookmark-form/config/serial.js +14 -0
- package/client/components/quick-commands/on-drop.js +6 -1
- package/client/components/quick-commands/quick-commands-box.jsx +18 -2
- package/client/components/quick-commands/quick-commands-list-form.jsx +15 -4
- package/client/components/quick-commands/quick-commands-list.jsx +18 -2
- package/client/components/session/session-control.jsx +30 -12
- package/client/components/session/session.jsx +5 -0
- package/client/components/setting-panel/setting-common.jsx +1 -0
- package/client/components/sftp/address-bookmark-item.jsx +15 -2
- package/client/components/sidebar/transport-ui.jsx +21 -14
- package/client/components/tabs/index.jsx +18 -3
- package/client/components/tabs/tab.jsx +45 -9
- package/client/components/tabs/tabs.styl +8 -0
- package/client/components/terminal/attach-addon-custom.js +147 -36
- package/client/components/terminal/highlight-addon.js +26 -8
- package/client/components/terminal/term-search.jsx +1 -1
- package/client/components/terminal/terminal.jsx +75 -1
- package/client/components/terminal-info/format-disks.js +45 -0
- package/client/components/terminal-info/run-cmd.jsx +2 -26
- package/client/components/tree-list/tree-list.jsx +57 -39
- package/client/css/basic.styl +14 -0
- package/client/css/mobile.styl +18 -0
- package/client/store/item.js +10 -6
- package/package.json +1 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* parse `df` output into disk list
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export default function formatDisks (str) {
|
|
6
|
+
if (!str) {
|
|
7
|
+
return {
|
|
8
|
+
disks: []
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const r = str.split('\n')
|
|
12
|
+
.slice(1)
|
|
13
|
+
.reduce((acc, s) => {
|
|
14
|
+
if (!s.trim()) {
|
|
15
|
+
return acc
|
|
16
|
+
}
|
|
17
|
+
const arr = s.trim().split(/ +/)
|
|
18
|
+
// df wraps long filesystem names (e.g. nfs "host:/very/long/export/path")
|
|
19
|
+
// onto a second line that starts with spaces and only holds the numeric
|
|
20
|
+
// columns. Merge such continuation lines back into the previous entry, so
|
|
21
|
+
// those disks (commonly nfs) are listed instead of dropped.
|
|
22
|
+
if (/^\s/.test(s) && acc.length) {
|
|
23
|
+
const prev = acc[acc.length - 1]
|
|
24
|
+
prev.size = arr[0]
|
|
25
|
+
prev.used = arr[1]
|
|
26
|
+
prev.avail = arr[2]
|
|
27
|
+
prev.usedPercent = arr[3]
|
|
28
|
+
prev.mount = arr[4]
|
|
29
|
+
return acc
|
|
30
|
+
}
|
|
31
|
+
acc.push({
|
|
32
|
+
filesystem: arr[0],
|
|
33
|
+
size: arr[1],
|
|
34
|
+
used: arr[2],
|
|
35
|
+
avail: arr[3],
|
|
36
|
+
usedPercent: arr[4],
|
|
37
|
+
mount: arr[5]
|
|
38
|
+
})
|
|
39
|
+
return acc
|
|
40
|
+
}, [])
|
|
41
|
+
.filter(d => d.filesystem && d.mount)
|
|
42
|
+
return {
|
|
43
|
+
disks: r
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -6,6 +6,7 @@ import { runCmd } from '../terminal/terminal-apis'
|
|
|
6
6
|
import { useEffect } from 'react'
|
|
7
7
|
import wait from '../../common/wait'
|
|
8
8
|
import parseInt10 from '../../common/parse-int10'
|
|
9
|
+
import formatDisks from './format-disks'
|
|
9
10
|
|
|
10
11
|
function formatActivities (str) {
|
|
11
12
|
if (!str) {
|
|
@@ -31,31 +32,6 @@ function formatActivities (str) {
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
function formatDisks (str) {
|
|
35
|
-
if (!str) {
|
|
36
|
-
return {
|
|
37
|
-
disks: []
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
const r = str.split('\n')
|
|
41
|
-
.slice(1)
|
|
42
|
-
.map(s => {
|
|
43
|
-
const arr = s.split(/ +/)
|
|
44
|
-
return {
|
|
45
|
-
filesystem: arr[0],
|
|
46
|
-
size: arr[1],
|
|
47
|
-
used: arr[2],
|
|
48
|
-
avail: arr[3],
|
|
49
|
-
usedPercent: arr[4],
|
|
50
|
-
mount: arr[5]
|
|
51
|
-
}
|
|
52
|
-
})
|
|
53
|
-
.filter(d => d.filesystem)
|
|
54
|
-
return {
|
|
55
|
-
disks: r
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
35
|
function formatCpu (str) {
|
|
60
36
|
if (!str) {
|
|
61
37
|
return {
|
|
@@ -206,7 +182,7 @@ export default (props) => {
|
|
|
206
182
|
},
|
|
207
183
|
{
|
|
208
184
|
name: 'disks',
|
|
209
|
-
cmd: 'df -
|
|
185
|
+
cmd: 'df -hP',
|
|
210
186
|
interval: 10000,
|
|
211
187
|
delay: 1600,
|
|
212
188
|
formatter: formatDisks
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from '../../common/constants'
|
|
15
15
|
import { getItemJSON, setItemJSON } from '../../common/safe-local-storage'
|
|
16
16
|
import findParentBySel from '../../common/find-parent'
|
|
17
|
+
import { isDropAfterHalf, setDropIndicator, clearDropIndicator } from '../../common/drop-position'
|
|
17
18
|
import copy from 'json-deep-copy'
|
|
18
19
|
import NewButtonsGroup from './bookmark-toolbar'
|
|
19
20
|
import findBookmarkGroupId from '../../common/find-bookmark-group-id'
|
|
@@ -479,8 +480,10 @@ export default class ItemListTree extends Component {
|
|
|
479
480
|
)
|
|
480
481
|
}
|
|
481
482
|
|
|
482
|
-
|
|
483
|
-
|
|
483
|
+
// resolve the .tree-item under the pointer and show a directional
|
|
484
|
+
// drop indicator (top edge = before, bottom edge = after).
|
|
485
|
+
// NOTE: preventDefault on dragover is required for onDrop to fire.
|
|
486
|
+
updateDropIndicator = e => {
|
|
484
487
|
let {
|
|
485
488
|
target
|
|
486
489
|
} = e
|
|
@@ -488,7 +491,12 @@ export default class ItemListTree extends Component {
|
|
|
488
491
|
if (tar) {
|
|
489
492
|
target = tar
|
|
490
493
|
}
|
|
491
|
-
target
|
|
494
|
+
setDropIndicator(target, isDropAfterHalf(e, target))
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
onDragEnter = e => {
|
|
498
|
+
e.preventDefault()
|
|
499
|
+
this.updateDropIndicator(e)
|
|
492
500
|
}
|
|
493
501
|
|
|
494
502
|
onDragLeave = e => {
|
|
@@ -500,25 +508,21 @@ export default class ItemListTree extends Component {
|
|
|
500
508
|
if (tar) {
|
|
501
509
|
target = tar
|
|
502
510
|
}
|
|
503
|
-
target
|
|
511
|
+
clearDropIndicator(target)
|
|
504
512
|
}
|
|
505
513
|
|
|
506
514
|
onDragOver = e => {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
} = e
|
|
510
|
-
const tar = findParentBySel(target, '.tree-item')
|
|
511
|
-
if (tar) {
|
|
512
|
-
target = tar
|
|
513
|
-
}
|
|
514
|
-
target.classList.add('item-dragover-top')
|
|
515
|
+
e.preventDefault()
|
|
516
|
+
this.updateDropIndicator(e)
|
|
515
517
|
}
|
|
516
518
|
|
|
517
519
|
onDrop = action(e => {
|
|
518
520
|
e.preventDefault()
|
|
519
|
-
const elems = document.querySelectorAll(
|
|
521
|
+
const elems = document.querySelectorAll(
|
|
522
|
+
'.tree-item.dnd-before, .tree-item.dnd-after'
|
|
523
|
+
)
|
|
520
524
|
elems.forEach(elem => {
|
|
521
|
-
elem
|
|
525
|
+
clearDropIndicator(elem)
|
|
522
526
|
})
|
|
523
527
|
let {
|
|
524
528
|
target
|
|
@@ -537,6 +541,9 @@ export default class ItemListTree extends Component {
|
|
|
537
541
|
const pidDrops = target.getAttribute('data-parent-id') || ''
|
|
538
542
|
const pidDropsArr = pidDrops.split('#')
|
|
539
543
|
const pidDrop = pidDropsArr[pidDropsArr.length - 1]
|
|
544
|
+
// bottom half of the target row => insert after it, so the last
|
|
545
|
+
// item/group in a list can receive a drop (append to the end).
|
|
546
|
+
const insertAfter = isDropAfterHalf(e, target)
|
|
540
547
|
// can not drag item to its own children
|
|
541
548
|
if (
|
|
542
549
|
(idDragged === 'default' &&
|
|
@@ -566,8 +573,9 @@ export default class ItemListTree extends Component {
|
|
|
566
573
|
if (indexDrop < 0) {
|
|
567
574
|
return
|
|
568
575
|
}
|
|
576
|
+
const insertIndex = insertAfter ? indexDrop + 1 : indexDrop
|
|
569
577
|
bookmarkGroups.splice(
|
|
570
|
-
|
|
578
|
+
insertIndex,
|
|
571
579
|
0,
|
|
572
580
|
dragItem
|
|
573
581
|
)
|
|
@@ -580,9 +588,13 @@ export default class ItemListTree extends Component {
|
|
|
580
588
|
)
|
|
581
589
|
: false
|
|
582
590
|
if (parentDrag) {
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
591
|
+
// remove from old parent IN PLACE: manate only reacts to in-place
|
|
592
|
+
// array mutations (the delete inside splice), not reassignment.
|
|
593
|
+
const fromArr = parentDrag.bookmarkGroupIds || []
|
|
594
|
+
const fromIdx = fromArr.indexOf(idDragged)
|
|
595
|
+
if (fromIdx >= 0) {
|
|
596
|
+
fromArr.splice(fromIdx, 1)
|
|
597
|
+
}
|
|
586
598
|
}
|
|
587
599
|
const parentDrop = pidDrop
|
|
588
600
|
? bookmarkGroups.find(
|
|
@@ -594,20 +606,18 @@ export default class ItemListTree extends Component {
|
|
|
594
606
|
if (!parentDrop) {
|
|
595
607
|
return
|
|
596
608
|
}
|
|
609
|
+
const dropArr = parentDrop.bookmarkGroupIds || []
|
|
597
610
|
if (!pidDrop) {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
idDragged
|
|
602
|
-
]
|
|
603
|
-
)
|
|
611
|
+
if (!dropArr.includes(idDragged)) {
|
|
612
|
+
dropArr.push(idDragged)
|
|
613
|
+
}
|
|
604
614
|
} else {
|
|
605
|
-
|
|
606
|
-
let index = arr.findIndex(item => item === idDrop)
|
|
615
|
+
let index = dropArr.findIndex(item => item === idDrop)
|
|
607
616
|
if (index < 0) {
|
|
608
617
|
index = 0
|
|
609
618
|
}
|
|
610
|
-
|
|
619
|
+
const insertIndex = insertAfter ? index + 1 : index
|
|
620
|
+
dropArr.splice(insertIndex, 0, idDragged)
|
|
611
621
|
}
|
|
612
622
|
} else {
|
|
613
623
|
const parentDrag = bookmarkGroups.find(
|
|
@@ -616,9 +626,13 @@ export default class ItemListTree extends Component {
|
|
|
616
626
|
if (!parentDrag) {
|
|
617
627
|
return
|
|
618
628
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
629
|
+
// remove from old parent IN PLACE: manate only reacts to in-place
|
|
630
|
+
// array mutations (the delete inside splice), not reassignment.
|
|
631
|
+
const fromArr = parentDrag.bookmarkIds || []
|
|
632
|
+
const fromIdx = fromArr.indexOf(idDragged)
|
|
633
|
+
if (fromIdx >= 0) {
|
|
634
|
+
fromArr.splice(fromIdx, 1)
|
|
635
|
+
}
|
|
622
636
|
const parentDrop = isGroupDrop
|
|
623
637
|
? bookmarkGroups.find(
|
|
624
638
|
item => item.id === idDrop
|
|
@@ -629,21 +643,25 @@ export default class ItemListTree extends Component {
|
|
|
629
643
|
if (!parentDrop) {
|
|
630
644
|
return
|
|
631
645
|
}
|
|
646
|
+
const dropArr = parentDrop.bookmarkIds || []
|
|
632
647
|
if (isGroupDrop) {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
idDragged
|
|
637
|
-
]
|
|
638
|
-
)
|
|
648
|
+
if (!dropArr.includes(idDragged)) {
|
|
649
|
+
dropArr.push(idDragged)
|
|
650
|
+
}
|
|
639
651
|
} else {
|
|
640
|
-
|
|
641
|
-
let index = arr.findIndex(item => item === idDrop)
|
|
652
|
+
let index = dropArr.findIndex(item => item === idDrop)
|
|
642
653
|
if (index < 0) {
|
|
643
654
|
index = 0
|
|
644
655
|
}
|
|
645
|
-
|
|
656
|
+
const insertIndex = insertAfter ? index + 1 : index
|
|
657
|
+
dropArr.splice(insertIndex, 0, idDragged)
|
|
646
658
|
}
|
|
659
|
+
// a manual bookmark reorder overrides any active sort, otherwise
|
|
660
|
+
// buildVisibleTreeRows would re-sort bookmarks and hide this order.
|
|
661
|
+
setItemJSON(treeSortLsKey, null)
|
|
662
|
+
this.setState({
|
|
663
|
+
sort: null
|
|
664
|
+
})
|
|
647
665
|
}
|
|
648
666
|
if (
|
|
649
667
|
isGroupDrag &&
|
package/client/css/basic.styl
CHANGED
|
@@ -42,6 +42,20 @@ a
|
|
|
42
42
|
.cap
|
|
43
43
|
text-transform capitalize
|
|
44
44
|
|
|
45
|
+
// shared drag-and-drop reorder indicators (vertical lists)
|
|
46
|
+
// first half of the target => insert before => top edge
|
|
47
|
+
// second half of the target => insert after => bottom edge
|
|
48
|
+
// (tabs use their own horizontal left/right line indicators)
|
|
49
|
+
.dnd-before
|
|
50
|
+
box-shadow inset 0 2px 0 0 var(--primary)
|
|
51
|
+
.dnd-after
|
|
52
|
+
box-shadow inset 0 -2px 0 0 var(--primary)
|
|
53
|
+
// antd table rows: box-shadow on <tr> is not rendered, apply on cells
|
|
54
|
+
tr.dnd-before > td
|
|
55
|
+
box-shadow inset 0 2px 0 0 var(--primary)
|
|
56
|
+
tr.dnd-after > td
|
|
57
|
+
box-shadow inset 0 -2px 0 0 var(--primary)
|
|
58
|
+
|
|
45
59
|
.sync-data-compare
|
|
46
60
|
.sync-suggest-action
|
|
47
61
|
color var(--success)
|
package/client/css/mobile.styl
CHANGED
|
@@ -29,6 +29,24 @@
|
|
|
29
29
|
// close icon always visible on mobile
|
|
30
30
|
.tab-close
|
|
31
31
|
display block
|
|
32
|
+
// keep a left drag grip for the custom title bar (not webapp, not system-ui),
|
|
33
|
+
// so the empty area left of the tab is draggable. mac needs extra room for
|
|
34
|
+
// the traffic-light window controls in the top-left.
|
|
35
|
+
&.not-system-ui.not-webapp
|
|
36
|
+
.mobile-tabs-inner
|
|
37
|
+
margin-left 20px !important
|
|
38
|
+
&.is-mac
|
|
39
|
+
.mobile-tabs-inner
|
|
40
|
+
margin-left 72px !important
|
|
41
|
+
// AppDrag wraps .mobile-tabs-inner under a custom title bar; make it the flex
|
|
42
|
+
// item so the tab layout is unchanged and the inner's left drag grip stays
|
|
43
|
+
// inside the draggable .app-drag box.
|
|
44
|
+
.tabs.mobile-tabs > .app-drag
|
|
45
|
+
flex 1
|
|
46
|
+
min-width 0
|
|
47
|
+
display flex
|
|
48
|
+
align-items center
|
|
49
|
+
overflow hidden
|
|
32
50
|
.mobile-tabs-extra
|
|
33
51
|
flex-shrink 0
|
|
34
52
|
display flex
|
package/client/store/item.js
CHANGED
|
@@ -82,18 +82,22 @@ export default Store => {
|
|
|
82
82
|
window.store[type] = arr
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
Store.prototype.adjustOrder = function (type, fromId, toId) {
|
|
85
|
+
Store.prototype.adjustOrder = function (type, fromId, toId, insertAfter = false) {
|
|
86
86
|
const { store } = window
|
|
87
87
|
const items = store.getItems(type)
|
|
88
88
|
const fromIndex = items.findIndex(t => t.id === fromId)
|
|
89
|
-
|
|
89
|
+
const toIndex = items.findIndex(t => t.id === toId)
|
|
90
90
|
if (fromIndex < 0 || toIndex < 0 || fromIndex === toIndex) {
|
|
91
91
|
return
|
|
92
92
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
// insertAfter lets the item land after the target, so the last
|
|
94
|
+
// item can receive a drop (insertIndex can reach items.length).
|
|
95
|
+
let insertIndex = insertAfter ? toIndex + 1 : toIndex
|
|
96
96
|
const [removed] = items.splice(fromIndex, 1)
|
|
97
|
-
|
|
97
|
+
// account for the index shift caused by the removal above
|
|
98
|
+
if (fromIndex < insertIndex) {
|
|
99
|
+
insertIndex = insertIndex - 1
|
|
100
|
+
}
|
|
101
|
+
items.splice(insertIndex, 0, removed)
|
|
98
102
|
}
|
|
99
103
|
}
|