@electerm/electerm-react 1.70.6 → 1.72.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/cache.js +56 -0
- package/client/common/constants.js +2 -0
- package/client/common/download.jsx +5 -7
- package/client/common/setting-list.js +27 -0
- package/client/components/ai/ai-cache.jsx +36 -0
- package/client/components/ai/ai-chat-history-item.jsx +1 -1
- package/client/components/ai/ai-chat.jsx +5 -40
- package/client/components/ai/ai-config-props.js +7 -0
- package/client/components/ai/ai-config.jsx +5 -14
- package/client/components/ai/ai.styl +0 -4
- package/client/components/ai/providers.js +2 -2
- package/client/components/batch-op/batch-op-entry.jsx +1 -1
- package/client/components/batch-op/batch-op.jsx +2 -2
- package/client/components/bookmark-form/form-ssh-common.jsx +2 -3
- package/client/components/bookmark-form/form-tabs.jsx +2 -2
- package/client/components/bookmark-form/render-connection-hopping.jsx +2 -2
- package/client/components/bookmark-form/render-delayed-scripts.jsx +4 -4
- package/client/components/bookmark-form/render-ssh-tunnel.jsx +2 -2
- package/client/components/bookmark-form/use-quick-commands.jsx +2 -2
- package/client/components/common/input-auto-focus.jsx +3 -11
- package/client/components/main/main.jsx +5 -0
- package/client/components/profile/profile-form-elem.jsx +1 -3
- package/client/components/quick-commands/quick-commands-form-elem.jsx +1 -3
- package/client/components/quick-commands/quick-commands-list-form.jsx +2 -2
- package/client/components/session/session.jsx +49 -14
- package/client/components/session/session.styl +10 -3
- package/client/components/session/sessions.jsx +1 -1
- package/client/components/setting-panel/keywords-form.jsx +36 -38
- package/client/components/setting-panel/setting-modal.jsx +2 -2
- package/client/components/setting-panel/tab-settings.jsx +26 -0
- package/client/components/sftp/address-bar.jsx +9 -2
- package/client/components/sftp/address-bookmark.jsx +4 -6
- package/client/components/sftp/keyword-filter.jsx +63 -0
- package/client/components/sftp/list-table-ui.jsx +7 -9
- package/client/components/sftp/sftp-entry.jsx +45 -8
- package/client/components/sftp/sftp.styl +6 -1
- package/client/components/shortcuts/shortcut-control.jsx +20 -0
- package/client/components/shortcuts/shortcut-editor.jsx +2 -2
- package/client/components/shortcuts/shortcuts.jsx +2 -2
- package/client/components/sidebar/info-modal.jsx +2 -2
- package/client/components/sidebar/transfer-list-control.jsx +18 -20
- package/client/components/ssh-config/ssh-config-item.jsx +2 -4
- package/client/components/ssh-config/ssh-config-load-notify.jsx +2 -2
- package/client/components/sys-menu/zoom.jsx +2 -2
- package/client/components/tabs/index.jsx +1 -1
- package/client/components/tabs/tab.jsx +3 -3
- package/client/components/terminal/cmd-item.jsx +32 -0
- package/client/components/terminal/command-tracker-addon.js +3 -1
- package/client/components/terminal/term-search.jsx +5 -6
- package/client/components/terminal/terminal-command-dropdown.jsx +303 -0
- package/client/components/terminal/terminal.jsx +84 -3
- package/client/components/terminal/terminal.styl +58 -0
- package/client/components/terminal-info/terminal-info.jsx +2 -2
- package/client/components/tree-list/tree-list.jsx +1 -1
- package/client/components/web/address-bar.jsx +2 -2
- package/client/store/common.js +27 -2
- package/client/store/init-state.js +3 -3
- package/client/store/item.js +2 -1
- package/client/store/setting.js +3 -2
- package/client/store/store.js +23 -24
- package/client/store/watch.js +7 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* terminal/sftp wrapper
|
|
3
3
|
*/
|
|
4
4
|
import { createRef } from 'react'
|
|
5
|
-
import { Component } from '
|
|
5
|
+
import { Component } from 'manate/react/class-components'
|
|
6
6
|
import Term from '../terminal/terminal.jsx'
|
|
7
7
|
import Sftp from '../sftp/sftp-entry'
|
|
8
8
|
import RdpSession from '../rdp/rdp-session'
|
|
@@ -12,7 +12,8 @@ import {
|
|
|
12
12
|
SearchOutlined,
|
|
13
13
|
FullscreenOutlined,
|
|
14
14
|
PaperClipOutlined,
|
|
15
|
-
CloseOutlined
|
|
15
|
+
CloseOutlined,
|
|
16
|
+
ApartmentOutlined
|
|
16
17
|
} from '@ant-design/icons'
|
|
17
18
|
import {
|
|
18
19
|
Tooltip,
|
|
@@ -50,7 +51,8 @@ export default class SessionWrapper extends Component {
|
|
|
50
51
|
splitSize: [50, 50],
|
|
51
52
|
sessionOptions: null,
|
|
52
53
|
sessionId: generate(),
|
|
53
|
-
delKeyPressed: false
|
|
54
|
+
delKeyPressed: false,
|
|
55
|
+
broadcastInput: false
|
|
54
56
|
}
|
|
55
57
|
props.tab.sshSftpSplitView = !!props.config.sshSftpSplitView
|
|
56
58
|
}
|
|
@@ -222,7 +224,7 @@ export default class SessionWrapper extends Component {
|
|
|
222
224
|
const update = {
|
|
223
225
|
pane
|
|
224
226
|
}
|
|
225
|
-
if (pane === paneMap.fileManager
|
|
227
|
+
if (pane === paneMap.fileManager) {
|
|
226
228
|
this.setState({
|
|
227
229
|
enableSftp: true
|
|
228
230
|
})
|
|
@@ -253,7 +255,8 @@ export default class SessionWrapper extends Component {
|
|
|
253
255
|
const {
|
|
254
256
|
sessionOptions,
|
|
255
257
|
sessionId,
|
|
256
|
-
sftpPathFollowSsh
|
|
258
|
+
sftpPathFollowSsh,
|
|
259
|
+
broadcastInput
|
|
257
260
|
} = this.state
|
|
258
261
|
const {
|
|
259
262
|
tab
|
|
@@ -312,7 +315,6 @@ export default class SessionWrapper extends Component {
|
|
|
312
315
|
}
|
|
313
316
|
|
|
314
317
|
const cls = pane === paneMap.terminal ||
|
|
315
|
-
pane === paneMap.ssh ||
|
|
316
318
|
(sshSftpSplitView && this.canSplitView())
|
|
317
319
|
? 'terms-box'
|
|
318
320
|
: 'terms-box hide'
|
|
@@ -326,6 +328,7 @@ export default class SessionWrapper extends Component {
|
|
|
326
328
|
...this.props,
|
|
327
329
|
sftpPathFollowSsh,
|
|
328
330
|
themeConfig,
|
|
331
|
+
broadcastInput,
|
|
329
332
|
pane,
|
|
330
333
|
...pick(
|
|
331
334
|
this,
|
|
@@ -423,7 +426,7 @@ export default class SessionWrapper extends Component {
|
|
|
423
426
|
const height = this.props.computeHeight(
|
|
424
427
|
this.props.height
|
|
425
428
|
)
|
|
426
|
-
const cls = pane === paneMap.fileManager ||
|
|
429
|
+
const cls = pane === paneMap.fileManager ||
|
|
427
430
|
(sshSftpSplitView && this.canSplitView())
|
|
428
431
|
? ''
|
|
429
432
|
: 'hide'
|
|
@@ -453,6 +456,12 @@ export default class SessionWrapper extends Component {
|
|
|
453
456
|
window.store.toggleTermFullscreen(true)
|
|
454
457
|
}
|
|
455
458
|
|
|
459
|
+
toggleBroadcastInput = () => {
|
|
460
|
+
this.setState({
|
|
461
|
+
broadcastInput: !this.state.broadcastInput
|
|
462
|
+
})
|
|
463
|
+
}
|
|
464
|
+
|
|
456
465
|
handleOpenSearch = () => {
|
|
457
466
|
refs.get('term-' + this.props.tab.id)?.toggleSearch()
|
|
458
467
|
}
|
|
@@ -462,7 +471,7 @@ export default class SessionWrapper extends Component {
|
|
|
462
471
|
return (
|
|
463
472
|
<Tooltip title={title} placement='bottomLeft'>
|
|
464
473
|
<SearchOutlined
|
|
465
|
-
className='mg1r icon-info
|
|
474
|
+
className='mg1r icon-info iblock pointer spliter'
|
|
466
475
|
onClick={this.handleOpenSearch}
|
|
467
476
|
/>
|
|
468
477
|
</Tooltip>
|
|
@@ -474,7 +483,7 @@ export default class SessionWrapper extends Component {
|
|
|
474
483
|
return (
|
|
475
484
|
<Tooltip title={title} placement='bottomLeft'>
|
|
476
485
|
<FullscreenOutlined
|
|
477
|
-
className='mg1r icon-info
|
|
486
|
+
className='mg1r icon-info iblock pointer spliter term-fullscreen-control1'
|
|
478
487
|
onClick={this.handleFullscreen}
|
|
479
488
|
/>
|
|
480
489
|
</Tooltip>
|
|
@@ -496,6 +505,23 @@ export default class SessionWrapper extends Component {
|
|
|
496
505
|
)
|
|
497
506
|
}
|
|
498
507
|
|
|
508
|
+
renderBroadcastIcon = () => {
|
|
509
|
+
const { broadcastInput } = this.state
|
|
510
|
+
const title = e('broadcastInput')
|
|
511
|
+
const iconProps = {
|
|
512
|
+
className: classnames('sess-icon pointer broadcast-icon', {
|
|
513
|
+
active: broadcastInput
|
|
514
|
+
}),
|
|
515
|
+
onClick: this.toggleBroadcastInput
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return (
|
|
519
|
+
<Tooltip title={title}>
|
|
520
|
+
<ApartmentOutlined {...iconProps} />
|
|
521
|
+
</Tooltip>
|
|
522
|
+
)
|
|
523
|
+
}
|
|
524
|
+
|
|
499
525
|
renderTermControls = () => {
|
|
500
526
|
const { props } = this
|
|
501
527
|
const { pane } = props.tab
|
|
@@ -515,10 +541,19 @@ export default class SessionWrapper extends Component {
|
|
|
515
541
|
return null
|
|
516
542
|
}
|
|
517
543
|
const title = e('sshSftpSplitView')
|
|
544
|
+
const {
|
|
545
|
+
sshSftpSplitView
|
|
546
|
+
} = this.props.tab
|
|
547
|
+
const cls = classnames(
|
|
548
|
+
'pointer sess-icon split-view-toggle',
|
|
549
|
+
{
|
|
550
|
+
active: sshSftpSplitView
|
|
551
|
+
}
|
|
552
|
+
)
|
|
518
553
|
return (
|
|
519
554
|
<Tooltip title={title} placement='bottomLeft'>
|
|
520
555
|
<span
|
|
521
|
-
className=
|
|
556
|
+
className={cls}
|
|
522
557
|
onClick={this.handleSshSftpSplitView}
|
|
523
558
|
>
|
|
524
559
|
<SplitViewIcon />
|
|
@@ -604,14 +639,13 @@ export default class SessionWrapper extends Component {
|
|
|
604
639
|
const checkProps = {
|
|
605
640
|
onClick: this.toggleCheckSftpPathFollowSsh,
|
|
606
641
|
className: classnames(
|
|
607
|
-
'sftp-follow-ssh-icon',
|
|
642
|
+
'sftp-follow-ssh-icon sess-icon pointer',
|
|
608
643
|
{
|
|
609
644
|
active: sftpPathFollowSsh
|
|
610
645
|
}
|
|
611
646
|
)
|
|
612
647
|
}
|
|
613
648
|
const isS = pane === paneMap.terminal ||
|
|
614
|
-
pane === paneMap.ssh ||
|
|
615
649
|
sshSftpSplitView
|
|
616
650
|
return (
|
|
617
651
|
<>
|
|
@@ -646,6 +680,7 @@ export default class SessionWrapper extends Component {
|
|
|
646
680
|
{this.renderPaneControl()}
|
|
647
681
|
{this.renderSftpPathFollowControl()}
|
|
648
682
|
{this.renderSplitToggle()}
|
|
683
|
+
{this.renderBroadcastIcon()}
|
|
649
684
|
{this.renderTermControls()}
|
|
650
685
|
</div>
|
|
651
686
|
)
|
|
@@ -670,8 +705,8 @@ export default class SessionWrapper extends Component {
|
|
|
670
705
|
}
|
|
671
706
|
const notSplitVew = !this.canSplitView() || !this.props.tab.sshSftpSplitView
|
|
672
707
|
const { pane } = this.props.tab
|
|
673
|
-
const show1 = notSplitVew &&
|
|
674
|
-
const show2 = notSplitVew &&
|
|
708
|
+
const show1 = notSplitVew && pane === paneMap.terminal
|
|
709
|
+
const show2 = notSplitVew && pane === paneMap.fileManager
|
|
675
710
|
const direction = this.getSplitDirection()
|
|
676
711
|
const layout = direction === 'leftRight' ? 'horizontal' : 'vertical'
|
|
677
712
|
const [size1, size2] = this.state.splitSize
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
position relative
|
|
17
17
|
display inline-block
|
|
18
18
|
|
|
19
|
-
.sftp-follow-ssh-icon
|
|
20
19
|
.type-tab
|
|
21
20
|
display inline-block
|
|
22
21
|
vertical-align middle
|
|
@@ -44,7 +43,6 @@
|
|
|
44
43
|
display none
|
|
45
44
|
.spliter
|
|
46
45
|
color text
|
|
47
|
-
font-size 16px
|
|
48
46
|
&:hover
|
|
49
47
|
color text-light
|
|
50
48
|
|
|
@@ -73,4 +71,13 @@
|
|
|
73
71
|
width auto !important
|
|
74
72
|
height auto !important
|
|
75
73
|
.not-split-view .ant-splitter-bar-dragger
|
|
76
|
-
display none
|
|
74
|
+
display none
|
|
75
|
+
|
|
76
|
+
.sess-icon
|
|
77
|
+
margin-right 10px
|
|
78
|
+
&.active
|
|
79
|
+
color warn
|
|
80
|
+
|
|
81
|
+
.split-view-toggle
|
|
82
|
+
&.active
|
|
83
|
+
color success
|
|
@@ -107,44 +107,42 @@ export default function KeywordForm (props) {
|
|
|
107
107
|
}, [props.keywordFormReset])
|
|
108
108
|
|
|
109
109
|
return (
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
>
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
{
|
|
122
|
-
(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
{
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
<
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
)
|
|
143
|
-
}
|
|
110
|
+
<Form
|
|
111
|
+
form={formChild}
|
|
112
|
+
onValuesChange={handleTrigger}
|
|
113
|
+
initialValues={formData}
|
|
114
|
+
onFinish={handleFinish}
|
|
115
|
+
>
|
|
116
|
+
<FormItem {...formItemLayout}>
|
|
117
|
+
<FormList
|
|
118
|
+
name='keywords'
|
|
119
|
+
>
|
|
120
|
+
{
|
|
121
|
+
(fields, { add, remove }, { errors }) => {
|
|
122
|
+
return (
|
|
123
|
+
<>
|
|
124
|
+
{
|
|
125
|
+
fields.map((field, i) => {
|
|
126
|
+
return renderItem(field, i, add, remove)
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
<FormItem>
|
|
130
|
+
<Button
|
|
131
|
+
type='dashed'
|
|
132
|
+
onClick={() => add({
|
|
133
|
+
color: 'red'
|
|
134
|
+
})}
|
|
135
|
+
icon={<PlusOutlined />}
|
|
136
|
+
>
|
|
137
|
+
{e('keyword')}
|
|
138
|
+
</Button>
|
|
139
|
+
</FormItem>
|
|
140
|
+
</>
|
|
141
|
+
)
|
|
144
142
|
}
|
|
145
|
-
|
|
146
|
-
</
|
|
147
|
-
</
|
|
148
|
-
</
|
|
143
|
+
}
|
|
144
|
+
</FormList>
|
|
145
|
+
</FormItem>
|
|
146
|
+
</Form>
|
|
149
147
|
)
|
|
150
148
|
}
|
|
@@ -104,7 +104,7 @@ export default auto(function SettingModalWrap (props) {
|
|
|
104
104
|
type: 'card'
|
|
105
105
|
}
|
|
106
106
|
return (
|
|
107
|
-
|
|
107
|
+
<>
|
|
108
108
|
<Tabs
|
|
109
109
|
{...tabsProps}
|
|
110
110
|
/>
|
|
@@ -141,7 +141,7 @@ export default auto(function SettingModalWrap (props) {
|
|
|
141
141
|
store={store}
|
|
142
142
|
settingTab={settingTab}
|
|
143
143
|
/>
|
|
144
|
-
|
|
144
|
+
</>
|
|
145
145
|
)
|
|
146
146
|
}
|
|
147
147
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { auto } from 'manate/react'
|
|
2
|
+
import { message } from 'antd'
|
|
2
3
|
import SettingCommon from './setting-common'
|
|
3
4
|
import SettingTerminal from './setting-terminal'
|
|
4
5
|
import SettingCol from './col'
|
|
6
|
+
import SettingAi from '../ai/ai-config'
|
|
5
7
|
import SyncSetting from '../setting-sync/setting-sync'
|
|
6
8
|
import Shortcuts from '../shortcuts/shortcuts'
|
|
7
9
|
import List from './list'
|
|
@@ -9,8 +11,10 @@ import {
|
|
|
9
11
|
settingMap,
|
|
10
12
|
settingSyncId,
|
|
11
13
|
settingTerminalId,
|
|
14
|
+
settingAiId,
|
|
12
15
|
settingShortcutsId
|
|
13
16
|
} from '../../common/constants'
|
|
17
|
+
import { aiConfigsArr } from '../ai/ai-config-props'
|
|
14
18
|
import { pick } from 'lodash-es'
|
|
15
19
|
|
|
16
20
|
export default auto(function TabSettings (props) {
|
|
@@ -26,6 +30,26 @@ export default auto(function TabSettings (props) {
|
|
|
26
30
|
store
|
|
27
31
|
} = props
|
|
28
32
|
let elem = null
|
|
33
|
+
|
|
34
|
+
function getInitialValues () {
|
|
35
|
+
const res = pick(props.store.config, aiConfigsArr)
|
|
36
|
+
if (!res.languageAI) {
|
|
37
|
+
res.languageAI = window.store.getLangName()
|
|
38
|
+
}
|
|
39
|
+
return res
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function handleConfigSubmit (values) {
|
|
43
|
+
window.store.updateConfig(values)
|
|
44
|
+
message.success('Saved')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const aiConfProps = {
|
|
48
|
+
initialValues: getInitialValues(),
|
|
49
|
+
onSubmit: handleConfigSubmit,
|
|
50
|
+
showAIConfig: true
|
|
51
|
+
}
|
|
52
|
+
|
|
29
53
|
const sid = settingItem.id
|
|
30
54
|
if (sid === settingSyncId) {
|
|
31
55
|
const syncProps = pick(store, [
|
|
@@ -37,6 +61,8 @@ export default auto(function TabSettings (props) {
|
|
|
37
61
|
'syncServerStatus'
|
|
38
62
|
])
|
|
39
63
|
elem = <SyncSetting {...syncProps} />
|
|
64
|
+
} else if (sid === settingAiId) {
|
|
65
|
+
elem = <SettingAi {...aiConfProps} />
|
|
40
66
|
} else if (sid === settingTerminalId) {
|
|
41
67
|
elem = <SettingTerminal {...listProps} config={store.config} />
|
|
42
68
|
} else if (sid === settingShortcutsId) {
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from '../../common/constants'
|
|
16
16
|
import classnames from 'classnames'
|
|
17
17
|
import AddrBookmark from './address-bookmark'
|
|
18
|
+
import KeywordFilter from './keyword-filter'
|
|
18
19
|
|
|
19
20
|
const e = window.translate
|
|
20
21
|
|
|
@@ -26,8 +27,13 @@ function renderAddonBefore (props, realPath) {
|
|
|
26
27
|
const isShow = props[`${type}ShowHiddenFile`]
|
|
27
28
|
const title = `${isShow ? e('hide') : e('show')} ${e('hfd')}`
|
|
28
29
|
const Icon = isShow ? EyeFilled : EyeInvisibleFilled
|
|
30
|
+
const keywordProps = {
|
|
31
|
+
keyword: props[`${type}Keyword`],
|
|
32
|
+
type,
|
|
33
|
+
updateKeyword: props.updateKeyword
|
|
34
|
+
}
|
|
29
35
|
return (
|
|
30
|
-
|
|
36
|
+
<>
|
|
31
37
|
<Tooltip
|
|
32
38
|
title={title}
|
|
33
39
|
placement='topLeft'
|
|
@@ -49,6 +55,7 @@ function renderAddonBefore (props, realPath) {
|
|
|
49
55
|
className='mg1r'
|
|
50
56
|
/>
|
|
51
57
|
</Tooltip>
|
|
58
|
+
<KeywordFilter {...keywordProps} />
|
|
52
59
|
<AddrBookmark
|
|
53
60
|
store={window.store}
|
|
54
61
|
realPath={realPath}
|
|
@@ -56,7 +63,7 @@ function renderAddonBefore (props, realPath) {
|
|
|
56
63
|
type={type}
|
|
57
64
|
onClickHistory={props.onClickHistory}
|
|
58
65
|
/>
|
|
59
|
-
|
|
66
|
+
</>
|
|
60
67
|
)
|
|
61
68
|
}
|
|
62
69
|
|
|
@@ -62,12 +62,10 @@ export default auto(function AddrBookmark (props) {
|
|
|
62
62
|
</div>
|
|
63
63
|
)
|
|
64
64
|
const title = (
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
/>
|
|
70
|
-
</div>
|
|
65
|
+
<PlusSquareOutlined
|
|
66
|
+
className='add-addr-bookmark'
|
|
67
|
+
onClick={handleAddAddr}
|
|
68
|
+
/>
|
|
71
69
|
)
|
|
72
70
|
return (
|
|
73
71
|
<Popover
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React, { useState, useRef, useEffect } from 'react'
|
|
2
|
+
import { Tooltip, Input } from 'antd'
|
|
3
|
+
import {
|
|
4
|
+
FilterOutlined,
|
|
5
|
+
CheckOutlined
|
|
6
|
+
} from '@ant-design/icons'
|
|
7
|
+
import classnames from 'classnames'
|
|
8
|
+
|
|
9
|
+
const e = window.translate
|
|
10
|
+
|
|
11
|
+
export default function KeywordFilter ({ keyword, type, updateKeyword }) {
|
|
12
|
+
const [text, setText] = useState(keyword)
|
|
13
|
+
const inputRef = useRef(null)
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
setText(keyword)
|
|
17
|
+
}, [keyword])
|
|
18
|
+
|
|
19
|
+
const handleInputChange = (e) => {
|
|
20
|
+
setText(e.target.value)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const applyFilter = () => {
|
|
24
|
+
updateKeyword(text, type)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const handleKeyPress = (e) => {
|
|
28
|
+
if (e.key === 'Enter') {
|
|
29
|
+
applyFilter()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const iconClass = classnames('keyword-filter-icon mg1r', {
|
|
34
|
+
active: !!keyword
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const inputProps = {
|
|
38
|
+
value: text,
|
|
39
|
+
onChange: handleInputChange,
|
|
40
|
+
addonBefore: <FilterOutlined />,
|
|
41
|
+
onKeyPress: handleKeyPress,
|
|
42
|
+
placeholder: e('keyword'),
|
|
43
|
+
className: 'keyword-filter-input',
|
|
44
|
+
addonAfter: <CheckOutlined onClick={applyFilter} />
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const tooltipContent = (
|
|
48
|
+
<Input
|
|
49
|
+
{...inputProps}
|
|
50
|
+
ref={inputRef}
|
|
51
|
+
/>
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if (!updateKeyword) {
|
|
55
|
+
return null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<Tooltip title={tooltipContent} trigger='click'>
|
|
60
|
+
<FilterOutlined className={iconClass} />
|
|
61
|
+
</Tooltip>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
@@ -298,15 +298,13 @@ export default class FileListTable extends Component {
|
|
|
298
298
|
<div
|
|
299
299
|
{...props}
|
|
300
300
|
>
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
/>
|
|
309
|
-
</div>
|
|
301
|
+
{this.props.renderEmptyFile(type)}
|
|
302
|
+
{this.renderParent(type)}
|
|
303
|
+
<PagedList
|
|
304
|
+
list={fileList}
|
|
305
|
+
renderItem={this.renderItem}
|
|
306
|
+
hasPager={hasPager}
|
|
307
|
+
/>
|
|
310
308
|
</div>
|
|
311
309
|
</div>
|
|
312
310
|
)
|
|
@@ -58,7 +58,7 @@ export default class Sftp extends Component {
|
|
|
58
58
|
if (
|
|
59
59
|
this.props.config.autoRefreshWhenSwitchToSftp &&
|
|
60
60
|
prevProps.pane !== this.props.pane &&
|
|
61
|
-
|
|
61
|
+
this.props.pane === paneMap.fileManager &&
|
|
62
62
|
this.state.inited
|
|
63
63
|
) {
|
|
64
64
|
this.onGoto(typeMap.local)
|
|
@@ -120,7 +120,8 @@ export default class Sftp extends Component {
|
|
|
120
120
|
[`${k}PathTemp`]: '',
|
|
121
121
|
[`${k}PathHistory`]: [],
|
|
122
122
|
[`${k}GidTree`]: {},
|
|
123
|
-
[`${k}UidTree`]: {}
|
|
123
|
+
[`${k}UidTree`]: {},
|
|
124
|
+
[`${k}Keyword`]: ''
|
|
124
125
|
})
|
|
125
126
|
return prev
|
|
126
127
|
}, {})
|
|
@@ -171,7 +172,13 @@ export default class Sftp extends Component {
|
|
|
171
172
|
isActive () {
|
|
172
173
|
return this.props.currentBatchTabId === this.props.tab.id &&
|
|
173
174
|
(this.props.pane === paneMap.fileManager ||
|
|
174
|
-
this.props.
|
|
175
|
+
this.props.sshSftpSplitView)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
updateKeyword = (keyword, type) => {
|
|
179
|
+
this.setState({
|
|
180
|
+
[`${type}Keyword`]: keyword
|
|
181
|
+
})
|
|
175
182
|
}
|
|
176
183
|
|
|
177
184
|
getCwdLocal = () => {
|
|
@@ -417,6 +424,20 @@ export default class Sftp extends Component {
|
|
|
417
424
|
}
|
|
418
425
|
|
|
419
426
|
modifier = (...args) => {
|
|
427
|
+
// Check if first argument is an object and contains path changes
|
|
428
|
+
if (args[0] && typeof args[0] === 'object') {
|
|
429
|
+
const updates = args[0]
|
|
430
|
+
|
|
431
|
+
// Clear respective keyword if path changes
|
|
432
|
+
if (updates.localPath !== undefined) {
|
|
433
|
+
updates.localKeyword = ''
|
|
434
|
+
}
|
|
435
|
+
if (updates.remotePath !== undefined) {
|
|
436
|
+
updates.remoteKeyword = ''
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Call setState with the modified arguments
|
|
420
441
|
runIdle(() => this.setState(...args))
|
|
421
442
|
}
|
|
422
443
|
|
|
@@ -433,11 +454,24 @@ export default class Sftp extends Component {
|
|
|
433
454
|
|
|
434
455
|
getFileList = type => {
|
|
435
456
|
const showHide = this.state[`${type}ShowHiddenFile`]
|
|
457
|
+
const keyword = this.state[`${type}Keyword`]
|
|
436
458
|
let list = this.state[type]
|
|
437
459
|
list = isArray(list) ? list : []
|
|
438
|
-
|
|
439
|
-
|
|
460
|
+
|
|
461
|
+
// Combine filtering for showHide and keyword in one loop
|
|
462
|
+
if (!showHide || keyword) {
|
|
463
|
+
const lowerKeyword = keyword.toLowerCase()
|
|
464
|
+
list = list.filter(f => {
|
|
465
|
+
if (!showHide && f.name.startsWith('.')) {
|
|
466
|
+
return false
|
|
467
|
+
}
|
|
468
|
+
if (keyword && !f.name.toLowerCase().includes(lowerKeyword)) {
|
|
469
|
+
return false
|
|
470
|
+
}
|
|
471
|
+
return true
|
|
472
|
+
})
|
|
440
473
|
}
|
|
474
|
+
|
|
441
475
|
return this.sort(
|
|
442
476
|
list,
|
|
443
477
|
type,
|
|
@@ -795,7 +829,8 @@ export default class Sftp extends Component {
|
|
|
795
829
|
})
|
|
796
830
|
}
|
|
797
831
|
this.setState({
|
|
798
|
-
[n]: np
|
|
832
|
+
[n]: np,
|
|
833
|
+
[`${type}Keyword`]: ''
|
|
799
834
|
}, () => this[`${type}List`](undefined, undefined, oldPath))
|
|
800
835
|
}
|
|
801
836
|
|
|
@@ -984,7 +1019,8 @@ export default class Sftp extends Component {
|
|
|
984
1019
|
'onInputBlur',
|
|
985
1020
|
'toggleShowHiddenFile',
|
|
986
1021
|
'goParent',
|
|
987
|
-
'onClickHistory'
|
|
1022
|
+
'onClickHistory',
|
|
1023
|
+
'updateKeyword'
|
|
988
1024
|
]
|
|
989
1025
|
),
|
|
990
1026
|
...pick(
|
|
@@ -996,7 +1032,8 @@ export default class Sftp extends Component {
|
|
|
996
1032
|
`${type}Path`,
|
|
997
1033
|
`${type}PathHistory`,
|
|
998
1034
|
`${type}InputFocus`,
|
|
999
|
-
'loadingSftp'
|
|
1035
|
+
'loadingSftp',
|
|
1036
|
+
`${type}Keyword`
|
|
1000
1037
|
]
|
|
1001
1038
|
)
|
|
1002
1039
|
}
|
|
@@ -35,6 +35,7 @@ class ShortcutControl extends React.PureComponent {
|
|
|
35
35
|
this.handleSftpKeyboardEvent(e)
|
|
36
36
|
// Then handle extended shortcuts
|
|
37
37
|
this.handleKeyboardEvent(e)
|
|
38
|
+
this.handleAiChat(e)
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
getActiveSftp = () => {
|
|
@@ -45,6 +46,25 @@ class ShortcutControl extends React.PureComponent {
|
|
|
45
46
|
return ref
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
handleAiChat = (e) => {
|
|
50
|
+
const { rightPanelTab } = window.store
|
|
51
|
+
if (rightPanelTab !== 'ai') {
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
const elem = document.activeElement
|
|
55
|
+
if (
|
|
56
|
+
e.ctrlKey &&
|
|
57
|
+
e.key === 'Enter' &&
|
|
58
|
+
!e.shiftKey &&
|
|
59
|
+
!e.altKey &&
|
|
60
|
+
!e.metaKey &&
|
|
61
|
+
elem?.tagName === 'TEXTAREA' &&
|
|
62
|
+
elem?.classList.contains('ai-chat-textarea')
|
|
63
|
+
) {
|
|
64
|
+
refsStatic.get('AIChat')?.handleSubmit()
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
48
68
|
// SFTP shortcuts handler
|
|
49
69
|
handleSftpKeyboardEvent = (e) => {
|
|
50
70
|
const activeSftp = this.getActiveSftp()
|
|
@@ -175,7 +175,7 @@ export default class ShortcutEdit extends PureComponent {
|
|
|
175
175
|
return null
|
|
176
176
|
}
|
|
177
177
|
return (
|
|
178
|
-
|
|
178
|
+
<>
|
|
179
179
|
<CheckOutlined
|
|
180
180
|
onClick={this.handleConfirm}
|
|
181
181
|
className='pointer'
|
|
@@ -184,7 +184,7 @@ export default class ShortcutEdit extends PureComponent {
|
|
|
184
184
|
onClick={this.handleCancel}
|
|
185
185
|
className='pointer mg1l'
|
|
186
186
|
/>
|
|
187
|
-
|
|
187
|
+
</>
|
|
188
188
|
)
|
|
189
189
|
}
|
|
190
190
|
|