@electerm/electerm-react 1.70.2 → 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 +50 -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/setting-sync/setting-sync-form.jsx +8 -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
|
}
|
|
@@ -76,6 +78,7 @@ export default class SessionWrapper extends Component {
|
|
|
76
78
|
this.editTab({
|
|
77
79
|
sshSftpSplitView: nv
|
|
78
80
|
})
|
|
81
|
+
window.store.triggerResize()
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
canSplitView = () => {
|
|
@@ -221,7 +224,7 @@ export default class SessionWrapper extends Component {
|
|
|
221
224
|
const update = {
|
|
222
225
|
pane
|
|
223
226
|
}
|
|
224
|
-
if (pane === paneMap.fileManager
|
|
227
|
+
if (pane === paneMap.fileManager) {
|
|
225
228
|
this.setState({
|
|
226
229
|
enableSftp: true
|
|
227
230
|
})
|
|
@@ -252,7 +255,8 @@ export default class SessionWrapper extends Component {
|
|
|
252
255
|
const {
|
|
253
256
|
sessionOptions,
|
|
254
257
|
sessionId,
|
|
255
|
-
sftpPathFollowSsh
|
|
258
|
+
sftpPathFollowSsh,
|
|
259
|
+
broadcastInput
|
|
256
260
|
} = this.state
|
|
257
261
|
const {
|
|
258
262
|
tab
|
|
@@ -311,7 +315,6 @@ export default class SessionWrapper extends Component {
|
|
|
311
315
|
}
|
|
312
316
|
|
|
313
317
|
const cls = pane === paneMap.terminal ||
|
|
314
|
-
pane === paneMap.ssh ||
|
|
315
318
|
(sshSftpSplitView && this.canSplitView())
|
|
316
319
|
? 'terms-box'
|
|
317
320
|
: 'terms-box hide'
|
|
@@ -325,6 +328,7 @@ export default class SessionWrapper extends Component {
|
|
|
325
328
|
...this.props,
|
|
326
329
|
sftpPathFollowSsh,
|
|
327
330
|
themeConfig,
|
|
331
|
+
broadcastInput,
|
|
328
332
|
pane,
|
|
329
333
|
...pick(
|
|
330
334
|
this,
|
|
@@ -422,7 +426,7 @@ export default class SessionWrapper extends Component {
|
|
|
422
426
|
const height = this.props.computeHeight(
|
|
423
427
|
this.props.height
|
|
424
428
|
)
|
|
425
|
-
const cls = pane === paneMap.fileManager ||
|
|
429
|
+
const cls = pane === paneMap.fileManager ||
|
|
426
430
|
(sshSftpSplitView && this.canSplitView())
|
|
427
431
|
? ''
|
|
428
432
|
: 'hide'
|
|
@@ -452,6 +456,12 @@ export default class SessionWrapper extends Component {
|
|
|
452
456
|
window.store.toggleTermFullscreen(true)
|
|
453
457
|
}
|
|
454
458
|
|
|
459
|
+
toggleBroadcastInput = () => {
|
|
460
|
+
this.setState({
|
|
461
|
+
broadcastInput: !this.state.broadcastInput
|
|
462
|
+
})
|
|
463
|
+
}
|
|
464
|
+
|
|
455
465
|
handleOpenSearch = () => {
|
|
456
466
|
refs.get('term-' + this.props.tab.id)?.toggleSearch()
|
|
457
467
|
}
|
|
@@ -461,7 +471,7 @@ export default class SessionWrapper extends Component {
|
|
|
461
471
|
return (
|
|
462
472
|
<Tooltip title={title} placement='bottomLeft'>
|
|
463
473
|
<SearchOutlined
|
|
464
|
-
className='mg1r icon-info
|
|
474
|
+
className='mg1r icon-info iblock pointer spliter'
|
|
465
475
|
onClick={this.handleOpenSearch}
|
|
466
476
|
/>
|
|
467
477
|
</Tooltip>
|
|
@@ -473,7 +483,7 @@ export default class SessionWrapper extends Component {
|
|
|
473
483
|
return (
|
|
474
484
|
<Tooltip title={title} placement='bottomLeft'>
|
|
475
485
|
<FullscreenOutlined
|
|
476
|
-
className='mg1r icon-info
|
|
486
|
+
className='mg1r icon-info iblock pointer spliter term-fullscreen-control1'
|
|
477
487
|
onClick={this.handleFullscreen}
|
|
478
488
|
/>
|
|
479
489
|
</Tooltip>
|
|
@@ -495,6 +505,23 @@ export default class SessionWrapper extends Component {
|
|
|
495
505
|
)
|
|
496
506
|
}
|
|
497
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
|
+
|
|
498
525
|
renderTermControls = () => {
|
|
499
526
|
const { props } = this
|
|
500
527
|
const { pane } = props.tab
|
|
@@ -514,10 +541,19 @@ export default class SessionWrapper extends Component {
|
|
|
514
541
|
return null
|
|
515
542
|
}
|
|
516
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
|
+
)
|
|
517
553
|
return (
|
|
518
554
|
<Tooltip title={title} placement='bottomLeft'>
|
|
519
555
|
<span
|
|
520
|
-
className=
|
|
556
|
+
className={cls}
|
|
521
557
|
onClick={this.handleSshSftpSplitView}
|
|
522
558
|
>
|
|
523
559
|
<SplitViewIcon />
|
|
@@ -603,14 +639,13 @@ export default class SessionWrapper extends Component {
|
|
|
603
639
|
const checkProps = {
|
|
604
640
|
onClick: this.toggleCheckSftpPathFollowSsh,
|
|
605
641
|
className: classnames(
|
|
606
|
-
'sftp-follow-ssh-icon',
|
|
642
|
+
'sftp-follow-ssh-icon sess-icon pointer',
|
|
607
643
|
{
|
|
608
644
|
active: sftpPathFollowSsh
|
|
609
645
|
}
|
|
610
646
|
)
|
|
611
647
|
}
|
|
612
648
|
const isS = pane === paneMap.terminal ||
|
|
613
|
-
pane === paneMap.ssh ||
|
|
614
649
|
sshSftpSplitView
|
|
615
650
|
return (
|
|
616
651
|
<>
|
|
@@ -645,6 +680,7 @@ export default class SessionWrapper extends Component {
|
|
|
645
680
|
{this.renderPaneControl()}
|
|
646
681
|
{this.renderSftpPathFollowControl()}
|
|
647
682
|
{this.renderSplitToggle()}
|
|
683
|
+
{this.renderBroadcastIcon()}
|
|
648
684
|
{this.renderTermControls()}
|
|
649
685
|
</div>
|
|
650
686
|
)
|
|
@@ -669,8 +705,8 @@ export default class SessionWrapper extends Component {
|
|
|
669
705
|
}
|
|
670
706
|
const notSplitVew = !this.canSplitView() || !this.props.tab.sshSftpSplitView
|
|
671
707
|
const { pane } = this.props.tab
|
|
672
|
-
const show1 = notSplitVew &&
|
|
673
|
-
const show2 = notSplitVew &&
|
|
708
|
+
const show1 = notSplitVew && pane === paneMap.terminal
|
|
709
|
+
const show2 = notSplitVew && pane === paneMap.fileManager
|
|
674
710
|
const direction = this.getSplitDirection()
|
|
675
711
|
const layout = direction === 'leftRight' ? 'horizontal' : 'vertical'
|
|
676
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) {
|
|
@@ -19,6 +19,10 @@ import ServerDataStatus from './server-data-status'
|
|
|
19
19
|
const FormItem = Form.Item
|
|
20
20
|
const e = window.translate
|
|
21
21
|
|
|
22
|
+
function trim (str) {
|
|
23
|
+
return str ? str.trim() : ''
|
|
24
|
+
}
|
|
25
|
+
|
|
22
26
|
export default function SyncForm (props) {
|
|
23
27
|
const [form] = Form.useForm()
|
|
24
28
|
const delta = useDelta(props.formData)
|
|
@@ -149,6 +153,7 @@ export default function SyncForm (props) {
|
|
|
149
153
|
<FormItem
|
|
150
154
|
label={createLabel('API Url')}
|
|
151
155
|
name='apiUrl'
|
|
156
|
+
normalize={trim}
|
|
152
157
|
rules={[{
|
|
153
158
|
max: 200, message: '200 chars max'
|
|
154
159
|
}]}
|
|
@@ -178,6 +183,7 @@ export default function SyncForm (props) {
|
|
|
178
183
|
<FormItem
|
|
179
184
|
label={gistLabel}
|
|
180
185
|
name='gistId'
|
|
186
|
+
normalize={trim}
|
|
181
187
|
rules={[{
|
|
182
188
|
max: 100, message: '100 chars max'
|
|
183
189
|
}]}
|
|
@@ -198,6 +204,7 @@ export default function SyncForm (props) {
|
|
|
198
204
|
label={syncPasswordLabel}
|
|
199
205
|
hasFeedback
|
|
200
206
|
name='syncPassword'
|
|
207
|
+
normalize={trim}
|
|
201
208
|
rules={[{
|
|
202
209
|
max: 100, message: '100 chars max'
|
|
203
210
|
}]}
|
|
@@ -226,6 +233,7 @@ export default function SyncForm (props) {
|
|
|
226
233
|
label={tokenLabel}
|
|
227
234
|
hasFeedback
|
|
228
235
|
name='token'
|
|
236
|
+
normalize={trim}
|
|
229
237
|
rules={[{
|
|
230
238
|
max: 1100, message: '1100 chars max'
|
|
231
239
|
}, {
|
|
@@ -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
|
}
|