@electerm/electerm-react 3.15.175 → 3.15.186

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 (37) hide show
  1. package/client/common/bookmark-schemas.js +2 -0
  2. package/client/common/drop-position.js +59 -0
  3. package/client/components/bookmark-form/bookmark-schema.js +2 -0
  4. package/client/components/bookmark-form/common/connection-hopping.jsx +33 -7
  5. package/client/components/bookmark-form/common/ssh-tunnel-form.jsx +25 -7
  6. package/client/components/bookmark-form/common/ssh-tunnels.jsx +1 -1
  7. package/client/components/bookmark-form/config/serial.js +14 -0
  8. package/client/components/main/term-fullscreen.styl +5 -0
  9. package/client/components/quick-commands/on-drop.js +6 -1
  10. package/client/components/quick-commands/quick-commands-box.jsx +18 -2
  11. package/client/components/quick-commands/quick-commands-list-form.jsx +15 -4
  12. package/client/components/quick-commands/quick-commands-list.jsx +18 -2
  13. package/client/components/session/session-control.jsx +331 -0
  14. package/client/components/session/session-control.styl +38 -0
  15. package/client/components/session/session.jsx +41 -246
  16. package/client/components/session/session.styl +1 -0
  17. package/client/components/sftp/address-bookmark-item.jsx +15 -2
  18. package/client/components/sftp/code-compare-utils.js +260 -0
  19. package/client/components/sftp/code-compare.jsx +183 -0
  20. package/client/components/sftp/code-compare.styl +13 -0
  21. package/client/components/sftp/file-compare-modal.jsx +87 -42
  22. package/client/components/shortcuts/shortcut-handler.js +20 -0
  23. package/client/components/sidebar/transport-ui.jsx +21 -14
  24. package/client/components/tabs/index.jsx +18 -3
  25. package/client/components/tabs/tab.jsx +27 -8
  26. package/client/components/tabs/tabs.styl +8 -0
  27. package/client/components/terminal/attach-addon-custom.js +147 -36
  28. package/client/components/terminal/highlight-addon.js +26 -8
  29. package/client/components/terminal/term-search.jsx +1 -1
  30. package/client/components/terminal/terminal.jsx +193 -2
  31. package/client/components/terminal/terminal.styl +0 -2
  32. package/client/components/terminal-info/run-cmd.jsx +1 -1
  33. package/client/components/tree-list/tree-list.jsx +57 -39
  34. package/client/css/basic.styl +14 -0
  35. package/client/css/mobile.styl +31 -9
  36. package/client/store/item.js +10 -6
  37. package/package.json +1 -1
@@ -0,0 +1,331 @@
1
+ /**
2
+ * Session control bar — pane tabs and action icons.
3
+ * On mobile a menu icon is shown; clicking it toggles the control icons.
4
+ */
5
+ import {
6
+ SearchOutlined,
7
+ FullscreenOutlined,
8
+ PaperClipOutlined,
9
+ CloseOutlined,
10
+ ApartmentOutlined,
11
+ MoreOutlined,
12
+ ColumnWidthOutlined,
13
+ LogoutOutlined
14
+ } from '@ant-design/icons'
15
+ import { Tooltip, Popover } from 'antd'
16
+ import classnames from 'classnames'
17
+ import {
18
+ paneMap,
19
+ connectionMap,
20
+ terminalSerialType
21
+ } from '../../common/constants'
22
+ import { SplitViewIcon } from '../icons/split-view'
23
+ import { HeartbeatIcon } from '../icons/heartbeat'
24
+ import './session-control.styl'
25
+
26
+ const e = window.translate
27
+
28
+ export default function SessionControl (props) {
29
+ const {
30
+ tab,
31
+ isMobile,
32
+ isDisabled,
33
+ isSshDisabled,
34
+ isNotTerminalType,
35
+ canSplitView,
36
+ sftpPathFollowSsh,
37
+ keepaliveEnabled,
38
+ broadcastInput,
39
+ wrapDisabled,
40
+ delKeyPressed,
41
+ hideDelKeyTip,
42
+ onChangePane,
43
+ toggleCheckSftpPathFollowSsh,
44
+ onSshSftpSplitView,
45
+ toggleKeepalive,
46
+ toggleBroadcastInput,
47
+ toggleWrap,
48
+ onFullscreen,
49
+ onOpenSearch,
50
+ onDismissDelKeyTip,
51
+ onExitGracefully
52
+ } = props
53
+
54
+ if (isNotTerminalType) {
55
+ return null
56
+ }
57
+
58
+ const isSsh = !!tab.authType
59
+ const isLocal = !isSsh && (tab.type === connectionMap.local || !tab.type)
60
+ const showSshFeatures = isSsh || isLocal
61
+
62
+ // ---- sub-renderers ----
63
+
64
+ function renderPaneControl () {
65
+ if (isDisabled) {
66
+ return null
67
+ }
68
+ const { sshSftpSplitView, pane } = tab
69
+ if (sshSftpSplitView && canSplitView) {
70
+ return null
71
+ }
72
+ const types = [
73
+ paneMap.terminal,
74
+ paneMap.fileManager
75
+ ]
76
+ const controls = [
77
+ isSsh ? paneMap.ssh : paneMap.terminal
78
+ ]
79
+ if (isSsh || isLocal) {
80
+ controls.push(isSsh ? paneMap.sftp : paneMap.fileManager)
81
+ }
82
+ return (
83
+ <div className='term-sftp-tabs fleft'>
84
+ {
85
+ controls.map((type, i) => {
86
+ const cls = classnames(
87
+ 'type-tab',
88
+ type,
89
+ {
90
+ active: types[i] === pane
91
+ }
92
+ )
93
+ return (
94
+ <span
95
+ className={cls}
96
+ key={type + '_' + i}
97
+ onClick={() => onChangePane(types[i])}
98
+ >
99
+ <span className='type-tab-txt'>
100
+ {e(type)}
101
+ <span className='type-tab-line' />
102
+ </span>
103
+ </span>
104
+ )
105
+ })
106
+ }
107
+ </div>
108
+ )
109
+ }
110
+
111
+ function renderDelTip (isS) {
112
+ if (!isS || hideDelKeyTip || !delKeyPressed) {
113
+ return null
114
+ }
115
+ return (
116
+ <div className='type-tab'>
117
+ <span className='mg1r'>Try <b>Shift + Backspace</b>?</span>
118
+ <CloseOutlined
119
+ onClick={onDismissDelKeyTip}
120
+ className='pointer'
121
+ />
122
+ </div>
123
+ )
124
+ }
125
+
126
+ function renderSftpPathFollowControl () {
127
+ if (isDisabled) {
128
+ return null
129
+ }
130
+ const { pane, enableSsh, sshSftpSplitView } = tab
131
+ const checkTxt = e('sftpPathFollowSsh')
132
+ const checkProps = {
133
+ onClick: toggleCheckSftpPathFollowSsh,
134
+ className: classnames(
135
+ 'sftp-follow-ssh-icon sess-icon pointer',
136
+ {
137
+ active: sftpPathFollowSsh
138
+ }
139
+ )
140
+ }
141
+ const isS = pane === paneMap.terminal ||
142
+ sshSftpSplitView
143
+ return (
144
+ <>
145
+ {
146
+ (isSsh && enableSsh) || isLocal
147
+ ? (
148
+ <Tooltip title={checkTxt}>
149
+ <span {...checkProps}>
150
+ <PaperClipOutlined />
151
+ </span>
152
+ </Tooltip>
153
+ )
154
+ : null
155
+ }
156
+ {renderDelTip(isS)}
157
+ </>
158
+ )
159
+ }
160
+
161
+ function renderSplitToggle () {
162
+ if (isMobile) {
163
+ return null
164
+ }
165
+ if (!canSplitView || isNotTerminalType || !showSshFeatures) {
166
+ return null
167
+ }
168
+ const title = e('sshSftpSplitView')
169
+ const { sshSftpSplitView } = tab
170
+ const cls = classnames(
171
+ 'pointer sess-icon split-view-toggle',
172
+ {
173
+ active: sshSftpSplitView
174
+ }
175
+ )
176
+ return (
177
+ <Tooltip title={title} placement='bottomLeft'>
178
+ <span
179
+ className={cls}
180
+ onClick={onSshSftpSplitView}
181
+ >
182
+ <SplitViewIcon />
183
+ </span>
184
+ </Tooltip>
185
+ )
186
+ }
187
+
188
+ function renderKeepaliveIcon () {
189
+ if (isSshDisabled || !showSshFeatures) {
190
+ return null
191
+ }
192
+ const title = e('keepalive')
193
+ const iconProps = {
194
+ className: classnames('sess-icon pointer keepalive-icon', {
195
+ active: keepaliveEnabled
196
+ }),
197
+ onClick: toggleKeepalive
198
+ }
199
+ return (
200
+ <Tooltip title={title}>
201
+ <HeartbeatIcon {...iconProps} />
202
+ </Tooltip>
203
+ )
204
+ }
205
+
206
+ function renderBroadcastIcon () {
207
+ if (isSshDisabled || !showSshFeatures) {
208
+ return null
209
+ }
210
+ const title = e('broadcastInput')
211
+ const iconProps = {
212
+ className: classnames('sess-icon pointer broadcast-icon', {
213
+ active: broadcastInput
214
+ }),
215
+ onClick: toggleBroadcastInput
216
+ }
217
+ return (
218
+ <Tooltip title={title}>
219
+ <ApartmentOutlined {...iconProps} />
220
+ </Tooltip>
221
+ )
222
+ }
223
+
224
+ function renderWrapIcon () {
225
+ const title = e(wrapDisabled ? 'enableWrap' : 'disableWrap')
226
+ const iconProps = {
227
+ className: classnames('sess-icon pointer wrap-toggle-icon', {
228
+ active: wrapDisabled
229
+ }),
230
+ onClick: toggleWrap
231
+ }
232
+ return (
233
+ <Tooltip title={title}>
234
+ <ColumnWidthOutlined {...iconProps} />
235
+ </Tooltip>
236
+ )
237
+ }
238
+
239
+ function renderExitGracefullyIcon () {
240
+ if (tab.type !== terminalSerialType) {
241
+ return null
242
+ }
243
+ const title = e('exitGracefully')
244
+ return (
245
+ <Tooltip title={title}>
246
+ <LogoutOutlined
247
+ className='sess-icon pointer exit-gracefully-icon'
248
+ onClick={onExitGracefully}
249
+ />
250
+ </Tooltip>
251
+ )
252
+ }
253
+
254
+ function renderSearchIcon () {
255
+ const title = e('search')
256
+ return (
257
+ <Tooltip title={title} placement='bottomLeft'>
258
+ <SearchOutlined
259
+ className='mg1r icon-info iblock pointer spliter'
260
+ onClick={onOpenSearch}
261
+ />
262
+ </Tooltip>
263
+ )
264
+ }
265
+
266
+ function renderFullscreenIcon () {
267
+ const title = e('fullscreen')
268
+ return (
269
+ <Tooltip title={title} placement='bottomLeft'>
270
+ <FullscreenOutlined
271
+ className='mg1r icon-info iblock pointer spliter fullscreen-control-icon'
272
+ onClick={onFullscreen}
273
+ />
274
+ </Tooltip>
275
+ )
276
+ }
277
+
278
+ function renderTermControls () {
279
+ const { pane } = tab
280
+ if (pane !== paneMap.terminal) {
281
+ return null
282
+ }
283
+ return (
284
+ <div className='fright term-controls'>
285
+ {renderFullscreenIcon()}
286
+ {renderSearchIcon()}
287
+ </div>
288
+ )
289
+ }
290
+
291
+ // ---- mobile ----
292
+
293
+ if (isMobile) {
294
+ const extraIcons = (
295
+ <div className='mobile-control-icons'>
296
+ {renderSftpPathFollowControl()}
297
+ {renderKeepaliveIcon()}
298
+ {renderBroadcastIcon()}
299
+ {renderWrapIcon()}
300
+ {renderExitGracefullyIcon()}
301
+ {renderTermControls()}
302
+ </div>
303
+ )
304
+ return (
305
+ <div className='terminal-control mobile-session-control'>
306
+ {renderPaneControl()}
307
+ <Popover
308
+ content={extraIcons}
309
+ trigger='click'
310
+ placement='bottomRight'
311
+ >
312
+ <MoreOutlined className='mobile-control-toggle pointer' />
313
+ </Popover>
314
+ </div>
315
+ )
316
+ }
317
+
318
+ // ---- desktop ----
319
+ return (
320
+ <div className='terminal-control fix'>
321
+ {renderPaneControl()}
322
+ {renderSftpPathFollowControl()}
323
+ {renderSplitToggle()}
324
+ {renderKeepaliveIcon()}
325
+ {renderBroadcastIcon()}
326
+ {renderWrapIcon()}
327
+ {renderExitGracefullyIcon()}
328
+ {renderTermControls()}
329
+ </div>
330
+ )
331
+ }
@@ -0,0 +1,38 @@
1
+ // Mobile session control: pane tabs always visible,
2
+ // other icons shown in a floating Popover overlay
3
+ .mobile-session-control
4
+ background var(--main)
5
+ line-height 32px
6
+ padding 0 10px
7
+ display flex
8
+ align-items center
9
+ // pane tabs take available space, menu icon sits at the end
10
+ .term-sftp-tabs
11
+ float none
12
+ display flex
13
+ align-items center
14
+ flex 1
15
+ min-width 0
16
+ .mobile-control-toggle
17
+ font-size 16px
18
+ line-height 32px
19
+ color var(--text)
20
+ flex-shrink 0
21
+ &:hover
22
+ color var(--text-light)
23
+
24
+ // Popover content — icons laid out in a row, wrapping as needed
25
+ .mobile-control-icons
26
+ display flex
27
+ flex-wrap wrap
28
+ align-items center
29
+ gap 4px 8px
30
+ max-width 280px
31
+ line-height 30px
32
+ .term-controls
33
+ float none
34
+ display flex
35
+ align-items center
36
+ margin-left 0
37
+ .sess-icon
38
+ margin-right 0
@@ -10,11 +10,7 @@ import VncSession from '../vnc/vnc-session'
10
10
  import WebSession from '../web/web-session.jsx'
11
11
  import SpiceSession from '../spice/spice-session'
12
12
  import {
13
- SearchOutlined,
14
- FullscreenOutlined,
15
- PaperClipOutlined,
16
- CloseOutlined,
17
- ApartmentOutlined
13
+ FullscreenOutlined
18
14
  } from '@ant-design/icons'
19
15
  import {
20
16
  Tooltip,
@@ -25,7 +21,6 @@ import copy from 'json-deep-copy'
25
21
  import classnames from 'classnames'
26
22
  import {
27
23
  paneMap,
28
- connectionMap,
29
24
  terminalRdpType,
30
25
  terminalVncType,
31
26
  terminalWebType,
@@ -33,10 +28,9 @@ import {
33
28
  terminalFtpType,
34
29
  terminalSpiceType
35
30
  } from '../../common/constants'
36
- import { SplitViewIcon } from '../icons/split-view'
37
31
  import { refs } from '../common/ref'
38
32
  import sanitizeFilename from '../../common/sanitize-filename.js'
39
- import { HeartbeatIcon } from '../icons/heartbeat'
33
+ import SessionControl from './session-control'
40
34
  import './session.styl'
41
35
 
42
36
  const e = window.translate
@@ -54,7 +48,8 @@ export default class SessionWrapper extends Component {
54
48
  sessionOptions: null,
55
49
  delKeyPressed: false,
56
50
  broadcastInput: false,
57
- keepaliveEnabled: false
51
+ keepaliveEnabled: false,
52
+ wrapDisabled: false
58
53
  }
59
54
  if (props.tab.sshSftpSplitView === undefined) {
60
55
  props.tab.sshSftpSplitView = !!props.config.sshSftpSplitView
@@ -495,20 +490,23 @@ export default class SessionWrapper extends Component {
495
490
  this.setState({ keepaliveEnabled: enabled })
496
491
  }
497
492
 
493
+ handleExitGracefully = () => {
494
+ refs.get('term-' + this.props.tab.id)?.exitGracefully()
495
+ }
496
+
498
497
  handleOpenSearch = () => {
499
498
  refs.get('term-' + this.props.tab.id)?.toggleSearch()
500
499
  }
501
500
 
502
- renderSearchIcon = () => {
503
- const title = e('search')
504
- return (
505
- <Tooltip title={title} placement='bottomLeft'>
506
- <SearchOutlined
507
- className='mg1r icon-info iblock pointer spliter'
508
- onClick={this.handleOpenSearch}
509
- />
510
- </Tooltip>
511
- )
501
+ toggleWrap = () => {
502
+ const termRef = refs.get('term-' + this.props.tab.id)
503
+ if (!termRef?.term) {
504
+ return
505
+ }
506
+ const next = !this.state.wrapDisabled
507
+ // DECAWM escape sequence: ?7l = disable wrap, ?7h = enable wrap
508
+ termRef.term.write(next ? '\x1b[?7l' : '\x1b[?7h')
509
+ this.setState({ wrapDisabled: next })
512
510
  }
513
511
 
514
512
  fullscreenIcon = () => {
@@ -523,237 +521,11 @@ export default class SessionWrapper extends Component {
523
521
  )
524
522
  }
525
523
 
526
- renderDelTip = (isSsh) => {
527
- if (!isSsh || this.props.hideDelKeyTip || !this.state.delKeyPressed) {
528
- return null
529
- }
530
- return (
531
- <div className='type-tab'>
532
- <span className='mg1r'>Try <b>Shift + Backspace</b>?</span>
533
- <CloseOutlined
534
- onClick={this.handleDismissDelKeyTip}
535
- className='pointer'
536
- />
537
- </div>
538
- )
539
- }
540
-
541
- renderKeepaliveIcon = () => {
542
- if (this.isSshDisabled()) {
543
- return null
544
- }
545
- const { keepaliveEnabled } = this.state
546
- const title = e('keepalive')
547
- const iconProps = {
548
- className: classnames('sess-icon pointer keepalive-icon', {
549
- active: keepaliveEnabled
550
- }),
551
- onClick: this.toggleKeepalive
552
- }
553
- return (
554
- <Tooltip title={title}>
555
- <HeartbeatIcon {...iconProps} />
556
- </Tooltip>
557
- )
558
- }
559
-
560
- renderBroadcastIcon = () => {
561
- if (
562
- this.isSshDisabled()
563
- ) {
564
- return null
565
- }
566
- const { broadcastInput } = this.state
567
- const title = e('broadcastInput')
568
- const iconProps = {
569
- className: classnames('sess-icon pointer broadcast-icon', {
570
- active: broadcastInput
571
- }),
572
- onClick: this.toggleBroadcastInput
573
- }
574
-
575
- return (
576
- <Tooltip title={title}>
577
- <ApartmentOutlined {...iconProps} />
578
- </Tooltip>
579
- )
580
- }
581
-
582
- renderTermControls = () => {
583
- const { props } = this
584
- const { pane } = props.tab
585
- if (pane !== paneMap.terminal) {
586
- return null
587
- }
588
- return (
589
- <div className='fright term-controls'>
590
- {this.fullscreenIcon()}
591
- {this.renderSearchIcon()}
592
- </div>
593
- )
594
- }
595
-
596
- renderSplitToggle = () => {
597
- if (window.store.isMobile) {
598
- return null
599
- }
600
- if (!this.canSplitView() || this.isNotTerminalType()) {
601
- return null
602
- }
603
- const title = e('sshSftpSplitView')
604
- const {
605
- sshSftpSplitView
606
- } = this.props.tab
607
- const cls = classnames(
608
- 'pointer sess-icon split-view-toggle',
609
- {
610
- active: sshSftpSplitView
611
- }
612
- )
613
- return (
614
- <Tooltip title={title} placement='bottomLeft'>
615
- <span
616
- className={cls}
617
- onClick={this.handleSshSftpSplitView}
618
- >
619
- <SplitViewIcon />
620
- </span>
621
- </Tooltip>
622
- )
623
- }
624
-
625
524
  isSsh = () => {
626
525
  const { tab } = this.props
627
526
  return tab.authType
628
527
  }
629
528
 
630
- renderPaneControl = () => {
631
- const {
632
- sshSftpSplitView
633
- } = this.props.tab
634
- if (this.isDisabled()) {
635
- return null
636
- }
637
- if (sshSftpSplitView && this.canSplitView()) {
638
- return null
639
- }
640
- const { props } = this
641
- const { tab } = props
642
- const { pane } = tab
643
- const termType = tab?.type
644
- const isSsh = tab.authType
645
- const isLocal = !isSsh && (termType === connectionMap.local || !termType)
646
- const types = [
647
- paneMap.terminal,
648
- paneMap.fileManager
649
- ]
650
- const controls = [
651
- isSsh ? paneMap.ssh : paneMap.terminal
652
- ]
653
- if (isSsh || isLocal) {
654
- controls.push(isSsh ? paneMap.sftp : paneMap.fileManager)
655
- }
656
- const simpleMapper = {
657
- [paneMap.terminal]: 'T',
658
- [paneMap.fileManager]: 'F',
659
- [paneMap.ssh]: 'T',
660
- [paneMap.sftp]: 'S'
661
- }
662
- return (
663
- <div className='term-sftp-tabs fleft'>
664
- {
665
- controls.map((type, i) => {
666
- const cls = classnames(
667
- 'type-tab',
668
- type,
669
- {
670
- active: types[i] === pane
671
- }
672
- )
673
- return (
674
- <span
675
- className={cls}
676
- key={type + '_' + i}
677
- onClick={() => this.onChangePane(types[i])}
678
- >
679
- <span className='type-tab-txt'>
680
- <span className='type-tab-full'>{e(type)}</span>
681
- <span className='type-tab-short'>{simpleMapper[type]}</span>
682
- <span className='type-tab-line' />
683
- </span>
684
- </span>
685
- )
686
- })
687
- }
688
- </div>
689
- )
690
- }
691
-
692
- renderSftpPathFollowControl = () => {
693
- if (this.isDisabled()) {
694
- return null
695
- }
696
- const {
697
- sftpPathFollowSsh
698
- } = this.state
699
- const { props } = this
700
- const { tab } = props
701
- const { pane, enableSsh, sshSftpSplitView } = tab
702
- const termType = tab?.type
703
- const isSsh = tab.authType
704
- const isLocal = !isSsh && (termType === connectionMap.local || !termType)
705
- const checkTxt = e('sftpPathFollowSsh')
706
- const checkProps = {
707
- onClick: this.toggleCheckSftpPathFollowSsh,
708
- className: classnames(
709
- 'sftp-follow-ssh-icon sess-icon pointer',
710
- {
711
- active: sftpPathFollowSsh
712
- }
713
- )
714
- }
715
- const isS = pane === paneMap.terminal ||
716
- sshSftpSplitView
717
- return (
718
- <>
719
- {
720
- (isSsh && enableSsh) || isLocal
721
- ? (
722
- <Tooltip title={checkTxt}>
723
- <span {...checkProps}>
724
- <PaperClipOutlined />
725
- </span>
726
- </Tooltip>
727
- )
728
- : null
729
- }
730
- {
731
- this.renderDelTip(isS)
732
- }
733
- </>
734
- )
735
- }
736
-
737
- renderControl = () => {
738
- if (
739
- this.isNotTerminalType()
740
- ) {
741
- return null
742
- }
743
- return (
744
- <div
745
- className='terminal-control fix'
746
- >
747
- {this.renderPaneControl()}
748
- {this.renderSftpPathFollowControl()}
749
- {this.renderSplitToggle()}
750
- {this.renderKeepaliveIcon()}
751
- {this.renderBroadcastIcon()}
752
- {this.renderTermControls()}
753
- </div>
754
- )
755
- }
756
-
757
529
  onSplitResize = (sizes) => {
758
530
  const direction = this.getSplitDirection()
759
531
  const {
@@ -850,7 +622,30 @@ export default class SessionWrapper extends Component {
850
622
  ref={this.domRef}
851
623
  {...divProps}
852
624
  >
853
- {this.renderControl()}
625
+ <SessionControl
626
+ tab={this.props.tab}
627
+ isMobile={window.store.isMobile}
628
+ isDisabled={this.isDisabled()}
629
+ isSshDisabled={this.isSshDisabled()}
630
+ isNotTerminalType={this.isNotTerminalType()}
631
+ canSplitView={this.canSplitView()}
632
+ sftpPathFollowSsh={this.state.sftpPathFollowSsh}
633
+ keepaliveEnabled={this.state.keepaliveEnabled}
634
+ broadcastInput={this.state.broadcastInput}
635
+ wrapDisabled={this.state.wrapDisabled}
636
+ delKeyPressed={this.state.delKeyPressed}
637
+ hideDelKeyTip={this.props.hideDelKeyTip}
638
+ onChangePane={(pane) => this.onChangePane(pane)}
639
+ toggleCheckSftpPathFollowSsh={this.toggleCheckSftpPathFollowSsh}
640
+ onSshSftpSplitView={this.handleSshSftpSplitView}
641
+ toggleKeepalive={this.toggleKeepalive}
642
+ toggleBroadcastInput={this.toggleBroadcastInput}
643
+ toggleWrap={this.toggleWrap}
644
+ onFullscreen={this.handleFullscreen}
645
+ onOpenSearch={this.handleOpenSearch}
646
+ onDismissDelKeyTip={this.handleDismissDelKeyTip}
647
+ onExitGracefully={this.handleExitGracefully}
648
+ />
854
649
  {this.renderViews()}
855
650
  </div>
856
651
  )
@@ -96,6 +96,7 @@
96
96
  &.active
97
97
  color var(--warn)
98
98
 
99
+ .wrap-toggle-icon
99
100
  .split-view-toggle
100
101
  &.active
101
102
  color var(--success)