@electerm/electerm-react 1.70.6 → 1.72.16

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 (72) hide show
  1. package/client/common/cache.js +56 -0
  2. package/client/common/constants.js +2 -0
  3. package/client/common/default-setting.js +2 -1
  4. package/client/common/download.jsx +5 -7
  5. package/client/common/setting-list.js +27 -0
  6. package/client/components/ai/ai-cache.jsx +36 -0
  7. package/client/components/ai/ai-chat-history-item.jsx +1 -1
  8. package/client/components/ai/ai-chat.jsx +5 -40
  9. package/client/components/ai/ai-config-props.js +7 -0
  10. package/client/components/ai/ai-config.jsx +5 -14
  11. package/client/components/ai/ai.styl +0 -4
  12. package/client/components/ai/providers.js +2 -2
  13. package/client/components/batch-op/batch-op-entry.jsx +1 -1
  14. package/client/components/batch-op/batch-op.jsx +2 -2
  15. package/client/components/bookmark-form/form-ssh-common.jsx +2 -3
  16. package/client/components/bookmark-form/form-tabs.jsx +2 -2
  17. package/client/components/bookmark-form/render-connection-hopping.jsx +2 -2
  18. package/client/components/bookmark-form/render-delayed-scripts.jsx +4 -4
  19. package/client/components/bookmark-form/render-ssh-tunnel.jsx +2 -2
  20. package/client/components/bookmark-form/sftp-enable.jsx +9 -0
  21. package/client/components/bookmark-form/ssh-form-ui.jsx +1 -0
  22. package/client/components/bookmark-form/ssh-form.jsx +3 -0
  23. package/client/components/bookmark-form/use-quick-commands.jsx +2 -2
  24. package/client/components/bookmark-form/x11.jsx +78 -9
  25. package/client/components/common/input-auto-focus.jsx +3 -11
  26. package/client/components/layout/layout.jsx +2 -1
  27. package/client/components/main/main.jsx +5 -0
  28. package/client/components/profile/profile-form-elem.jsx +1 -3
  29. package/client/components/quick-commands/quick-commands-form-elem.jsx +1 -3
  30. package/client/components/quick-commands/quick-commands-list-form.jsx +2 -2
  31. package/client/components/session/session.jsx +80 -19
  32. package/client/components/session/session.styl +10 -3
  33. package/client/components/session/sessions.jsx +2 -1
  34. package/client/components/setting-panel/keywords-form.jsx +36 -38
  35. package/client/components/setting-panel/setting-modal.jsx +2 -2
  36. package/client/components/setting-panel/setting-terminal.jsx +2 -1
  37. package/client/components/setting-panel/tab-settings.jsx +26 -0
  38. package/client/components/sftp/address-bar.jsx +9 -2
  39. package/client/components/sftp/address-bookmark.jsx +4 -6
  40. package/client/components/sftp/file-item.jsx +1 -1
  41. package/client/components/sftp/file-read.js +14 -19
  42. package/client/components/sftp/keyword-filter.jsx +63 -0
  43. package/client/components/sftp/list-table-ui.jsx +7 -9
  44. package/client/components/sftp/sftp-entry.jsx +46 -9
  45. package/client/components/sftp/sftp.styl +6 -1
  46. package/client/components/sftp/transfer-conflict-store.jsx +1 -1
  47. package/client/components/shortcuts/shortcut-control.jsx +20 -0
  48. package/client/components/shortcuts/shortcut-editor.jsx +2 -2
  49. package/client/components/shortcuts/shortcuts.jsx +2 -2
  50. package/client/components/sidebar/info-modal.jsx +2 -2
  51. package/client/components/sidebar/transfer-list-control.jsx +18 -20
  52. package/client/components/ssh-config/ssh-config-item.jsx +2 -4
  53. package/client/components/ssh-config/ssh-config-load-notify.jsx +2 -2
  54. package/client/components/sys-menu/zoom.jsx +2 -2
  55. package/client/components/tabs/index.jsx +1 -1
  56. package/client/components/tabs/tab.jsx +3 -3
  57. package/client/components/terminal/cmd-item.jsx +32 -0
  58. package/client/components/terminal/command-tracker-addon.js +3 -1
  59. package/client/components/terminal/term-search.jsx +5 -6
  60. package/client/components/terminal/terminal-command-dropdown.jsx +303 -0
  61. package/client/components/terminal/terminal.jsx +88 -8
  62. package/client/components/terminal/terminal.styl +58 -0
  63. package/client/components/terminal-info/terminal-info.jsx +2 -2
  64. package/client/components/tree-list/tree-list.jsx +1 -1
  65. package/client/components/web/address-bar.jsx +2 -2
  66. package/client/store/common.js +27 -2
  67. package/client/store/init-state.js +3 -3
  68. package/client/store/item.js +2 -1
  69. package/client/store/setting.js +3 -2
  70. package/client/store/store.js +23 -24
  71. package/client/store/watch.js +7 -1
  72. package/package.json +1 -1
@@ -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
- <div>
302
- {this.props.renderEmptyFile(type)}
303
- {this.renderParent(type)}
304
- <PagedList
305
- list={fileList}
306
- renderItem={this.renderItem}
307
- hasPager={hasPager}
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
- (this.props.pane === paneMap.fileManager || this.props.pane.sftp) &&
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.pane === paneMap.sftp || this.props.sshSftpSplitView)
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
- if (!showHide) {
439
- list = list.filter(f => !/^\./.test(f.name))
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,
@@ -702,7 +736,7 @@ export default class Sftp extends Component {
702
736
  const local = []
703
737
  for (const name of locals) {
704
738
  const p = resolve(localPath, name)
705
- const fileObj = await getLocalFileInfo(p)
739
+ const fileObj = await getLocalFileInfo(p).catch(console.log)
706
740
  if (fileObj) {
707
741
  local.push(fileObj)
708
742
  }
@@ -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
  }
@@ -175,4 +175,9 @@
175
175
  position relative
176
176
  .file-header-context-menu
177
177
  position fixed
178
- z-index 999
178
+ z-index 999
179
+
180
+ .keyword-filter-icon
181
+ &:hover
182
+ &.active
183
+ color success
@@ -39,7 +39,7 @@ export default class TransferConflictStore extends PureComponent {
39
39
  }
40
40
 
41
41
  localCheckExist = (path) => {
42
- return getLocalFileInfo(path)
42
+ return getLocalFileInfo(path).catch(console.log)
43
43
  }
44
44
 
45
45
  remoteCheckExist = (path, sessionId) => {
@@ -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
- <div>
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
- </div>
187
+ </>
188
188
  )
189
189
  }
190
190
 
@@ -140,7 +140,7 @@ export default class Shortcuts extends PureComponent {
140
140
  rowKey: 'id'
141
141
  }
142
142
  return (
143
- <div>
143
+ <>
144
144
  <Table
145
145
  {...props}
146
146
  />
@@ -151,7 +151,7 @@ export default class Shortcuts extends PureComponent {
151
151
  {e('resetAllToDefault')}
152
152
  </Button>
153
153
  </div>
154
- </div>
154
+ </>
155
155
  )
156
156
  }
157
157
  }
@@ -119,7 +119,7 @@ export default auto(function InfoModal (props) {
119
119
  key: infoTabs.info,
120
120
  label: e('about'),
121
121
  children: (
122
- <div>
122
+ <>
123
123
  <LogoElem />
124
124
  <p className='mg2b'>{e('desc')}</p>
125
125
  <RunningTime />
@@ -187,7 +187,7 @@ export default auto(function InfoModal (props) {
187
187
  </Link>
188
188
  </p>
189
189
  {renderCheckUpdate()}
190
- </div>
190
+ </>
191
191
  )
192
192
  },
193
193
  {
@@ -137,26 +137,24 @@ export default class TransferModalUI extends Component {
137
137
  ...groups
138
138
  ]
139
139
  return (
140
- <div>
141
- <Select
142
- value={this.state.filter}
143
- onChange={this.handleFilter}
144
- popupMatchSelectWidth={false}
145
- >
146
- {
147
- all.map(item => {
148
- return (
149
- <Option
150
- key={item.id}
151
- value={item.id}
152
- >
153
- {item.title}
154
- </Option>
155
- )
156
- })
157
- }
158
- </Select>
159
- </div>
140
+ <Select
141
+ value={this.state.filter}
142
+ onChange={this.handleFilter}
143
+ popupMatchSelectWidth={false}
144
+ >
145
+ {
146
+ all.map(item => {
147
+ return (
148
+ <Option
149
+ key={item.id}
150
+ value={item.id}
151
+ >
152
+ {item.title}
153
+ </Option>
154
+ )
155
+ })
156
+ }
157
+ </Select>
160
158
  )
161
159
  }
162
160
 
@@ -16,10 +16,8 @@ export default function SshConfigItem (props) {
16
16
 
17
17
  return (
18
18
  <Tooltip title={generateTooltipContent(item)}>
19
- <div>
20
- <div className='elli pd1y pd2x'>
21
- ssh {item.title}
22
- </div>
19
+ <div className='elli pd1y pd2x'>
20
+ ssh {item.title}
23
21
  </div>
24
22
  </Tooltip>
25
23
  )
@@ -25,7 +25,7 @@ function showNotification () {
25
25
  placement: 'bottom',
26
26
  key: 'sshConfigNotify',
27
27
  description: (
28
- <div>
28
+ <>
29
29
  <p>{e('sshConfigNotice')}</p>
30
30
  <Button type='primary' onClick={handleLoad} className='mg1r mg1b'>
31
31
  {e('import')}
@@ -33,7 +33,7 @@ function showNotification () {
33
33
  <Button onClick={handleIgnore} className='mg1r mg1b'>
34
34
  {e('ignore')}
35
35
  </Button>
36
- </div>
36
+ </>
37
37
  )
38
38
  })
39
39
  }
@@ -20,7 +20,7 @@ export default function ZoomMenu (props) {
20
20
  max={500}
21
21
  addonAfter='%'
22
22
  addonBefore={
23
- <div>
23
+ <>
24
24
  <PlusCircleOutlined
25
25
  onClick={() => store.zoom(0.25, true)}
26
26
  className='mg1r pointer font16'
@@ -29,7 +29,7 @@ export default function ZoomMenu (props) {
29
29
  onClick={() => store.zoom(-0.25, true)}
30
30
  className='pointer font16'
31
31
  />
32
- </div>
32
+ </>
33
33
  }
34
34
  />
35
35
  )
@@ -2,7 +2,7 @@
2
2
  * session tabs component
3
3
  */
4
4
 
5
- import { Component } from '../common/component'
5
+ import { Component } from 'manate/react/class-components'
6
6
  import React from 'react'
7
7
  import runIdle from '../../common/run-idle'
8
8
  import { throttle } from 'lodash-es'
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import { createRef } from 'react'
6
- import { Component } from '../common/component'
6
+ import { Component } from 'manate/react/class-components'
7
7
  import { refs } from '../common/ref'
8
8
  import {
9
9
  CloseOutlined,
@@ -373,10 +373,10 @@ class Tab extends Component {
373
373
  return <div key={tunnel}>{tunnel}</div>
374
374
  })
375
375
  return (
376
- <div>
376
+ <>
377
377
  <div>{title}</div>
378
378
  {list}
379
- </div>
379
+ </>
380
380
  )
381
381
  }
382
382
 
@@ -0,0 +1,32 @@
1
+ import React from 'react'
2
+ import { CloseCircleOutlined } from '@ant-design/icons'
3
+
4
+ const SuggestionItem = ({ item, onSelect, onDelete }) => {
5
+ const handleClick = () => {
6
+ onSelect(item)
7
+ }
8
+
9
+ const handleDelete = (e) => {
10
+ e.stopPropagation()
11
+ onDelete(item)
12
+ }
13
+
14
+ return (
15
+ <div className='suggestion-item'>
16
+ <span className='suggestion-command' onClick={handleClick}>
17
+ {item.command}
18
+ </span>
19
+ <span className='suggestion-type'>
20
+ {item.type}
21
+ </span>
22
+ {item.type === 'H' && (
23
+ <CloseCircleOutlined
24
+ className='suggestion-delete'
25
+ onClick={handleDelete}
26
+ />
27
+ )}
28
+ </div>
29
+ )
30
+ }
31
+
32
+ export default SuggestionItem
@@ -45,7 +45,9 @@ export class CommandTrackerAddon {
45
45
  // This is now our internal handler
46
46
  _handleKey = (e) => {
47
47
  const { key } = e
48
- if (key === 'Enter') {
48
+ if (e.ctrlKey && key.toLowerCase() === 'c') {
49
+ this.clearCommand()
50
+ } else if (key === 'Enter') {
49
51
  // Command executed, reset
50
52
  this.currentCommand = this.activeCommand
51
53
  this.activeCommand = ''
@@ -127,14 +127,14 @@ export default class TermSearch extends PureComponent {
127
127
 
128
128
  renderAfter = () => {
129
129
  return (
130
- <div>
130
+ <>
131
131
  {
132
132
  this.renderMatchData()
133
133
  }
134
134
  {
135
135
  this.searchActions.map(this.renderSearchAction)
136
136
  }
137
- </div>
137
+ </>
138
138
  )
139
139
  }
140
140
 
@@ -174,11 +174,11 @@ export default class TermSearch extends PureComponent {
174
174
 
175
175
  renderSuffix = () => {
176
176
  return (
177
- <div>
177
+ <>
178
178
  {
179
179
  this.searchControls.map(this.renderSearchControl)
180
180
  }
181
- </div>
181
+ </>
182
182
  )
183
183
  }
184
184
 
@@ -191,8 +191,7 @@ export default class TermSearch extends PureComponent {
191
191
  if (
192
192
  !termSearchOpen ||
193
193
  !currentTab ||
194
- currentTab.pane === paneMap.fileManager ||
195
- currentTab.pane === paneMap.sftp
194
+ currentTab.pane === paneMap.fileManager
196
195
  ) {
197
196
  return null
198
197
  }