@electerm/electerm-react 3.1.26 → 3.3.8

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 (61) hide show
  1. package/client/common/constants.js +1 -3
  2. package/client/common/db.js +4 -2
  3. package/client/components/ai/ai-history.jsx +4 -4
  4. package/client/components/batch-op/batch-op-alert.jsx +42 -0
  5. package/client/components/batch-op/batch-op-editor.jsx +202 -0
  6. package/client/components/batch-op/batch-op-logs.jsx +53 -0
  7. package/client/components/batch-op/batch-op-runner.jsx +315 -0
  8. package/client/components/bookmark-form/ai-bookmark-form.jsx +2 -1
  9. package/client/components/bookmark-form/bookmark-from-history-modal.jsx +2 -1
  10. package/client/components/bookmark-form/common/bookmark-select.jsx +18 -2
  11. package/client/components/bookmark-form/common/connection-hopping-form.jsx +153 -0
  12. package/client/components/bookmark-form/common/connection-hopping.jsx +136 -129
  13. package/client/components/common/auto-check-update.jsx +31 -0
  14. package/client/components/common/notification.styl +1 -1
  15. package/client/components/file-transfer/conflict-resolve.jsx +3 -0
  16. package/client/components/footer/batch-input.jsx +10 -7
  17. package/client/components/main/error-wrapper.jsx +18 -7
  18. package/client/components/main/main.jsx +6 -7
  19. package/client/components/quick-commands/qm.styl +0 -2
  20. package/client/components/quick-commands/quick-commands-list-form.jsx +1 -1
  21. package/client/components/setting-panel/hotkey.jsx +9 -1
  22. package/client/components/setting-panel/list.jsx +0 -1
  23. package/client/components/setting-panel/list.styl +4 -0
  24. package/client/components/setting-panel/setting-modal.jsx +53 -47
  25. package/client/components/setting-sync/auto-sync.jsx +53 -0
  26. package/client/components/setting-sync/data-import.jsx +69 -8
  27. package/client/components/sftp/address-bar.jsx +7 -1
  28. package/client/components/shortcuts/shortcut-editor.jsx +4 -2
  29. package/client/components/sidebar/bookmark-select.jsx +3 -2
  30. package/client/components/sidebar/history-item.jsx +3 -1
  31. package/client/components/sidebar/history.jsx +1 -0
  32. package/client/components/sidebar/index.jsx +0 -9
  33. package/client/components/tabs/add-btn-menu.jsx +1 -1
  34. package/client/components/tabs/add-btn.jsx +9 -15
  35. package/client/components/tabs/quick-connect.jsx +6 -10
  36. package/client/components/terminal/attach-addon-custom.js +86 -0
  37. package/client/components/terminal/cmd-item.jsx +13 -3
  38. package/client/components/terminal/drop-file-modal.jsx +57 -0
  39. package/client/components/terminal/terminal-command-dropdown.jsx +91 -13
  40. package/client/components/terminal/terminal.jsx +107 -10
  41. package/client/components/terminal/terminal.styl +9 -0
  42. package/client/components/tree-list/tree-list-item.jsx +0 -1
  43. package/client/components/tree-list/tree-list.jsx +115 -10
  44. package/client/components/tree-list/tree-list.styl +3 -0
  45. package/client/components/tree-list/tree-search.jsx +9 -1
  46. package/client/components/vnc/vnc-session.jsx +2 -0
  47. package/client/components/widgets/widget-control.jsx +3 -0
  48. package/client/components/widgets/widget-form.jsx +6 -0
  49. package/client/components/widgets/widget-instance.jsx +26 -7
  50. package/client/css/includes/box.styl +3 -0
  51. package/client/store/common.js +0 -28
  52. package/client/store/init-state.js +2 -1
  53. package/client/store/load-data.js +6 -4
  54. package/client/store/mcp-handler.js +20 -2
  55. package/client/store/sync.js +25 -1
  56. package/client/store/tab.js +1 -1
  57. package/client/store/watch.js +10 -18
  58. package/client/store/widgets.js +54 -0
  59. package/client/views/index.pug +1 -2
  60. package/package.json +1 -1
  61. package/client/components/batch-op/batch-op.jsx +0 -694
@@ -6,7 +6,6 @@ import UpdateCheck from './upgrade'
6
6
  import SettingModal from '../setting-panel/setting-modal'
7
7
  import TextEditor from '../text-editor/text-editor'
8
8
  import Sidebar from '../sidebar'
9
- import BatchOp from '../batch-op/batch-op'
10
9
  import CssOverwrite from '../bg/css-overwrite'
11
10
  import UiTheme from './ui-theme'
12
11
  import CustomCss from '../bg/custom-css.jsx'
@@ -34,6 +33,9 @@ import MoveItemModal from '../tree-list/move-item-modal'
34
33
  import InputContextMenu from '../common/input-context-menu'
35
34
  import WorkspaceSaveModal from '../tabs/workspace-save-modal'
36
35
  import BookmarkFromHistoryModal from '../bookmark-form/bookmark-from-history-modal'
36
+ import AutoSync from '../setting-sync/auto-sync'
37
+ import AutoCheckUpdate from '../common/auto-check-update'
38
+ import BatchOpRunner from '../batch-op/batch-op-runner'
37
39
  import { pick } from 'lodash-es'
38
40
  import deepCopy from 'json-deep-copy'
39
41
  import './wrapper.styl'
@@ -184,11 +186,6 @@ export default auto(function Index (props) {
184
186
  fileTransferChanged: JSON.stringify(copiedTransfer),
185
187
  fileTransfers: copiedTransfer
186
188
  }
187
- const batchOpProps = {
188
- transferHistory,
189
- showModal: store.showModal,
190
- innerWidth: store.innerWidth
191
- }
192
189
  const resProps = {
193
190
  resolutions: deepCopy(store.resolutions),
194
191
  openResolutionEdit
@@ -263,7 +260,6 @@ export default auto(function Index (props) {
263
260
  />
264
261
  <FileInfoModal />
265
262
  <SettingModal store={store} />
266
- <BatchOp {...batchOpProps} />
267
263
  <MoveItemModal store={store} />
268
264
  <div
269
265
  id='outside-context'
@@ -295,9 +291,12 @@ export default auto(function Index (props) {
295
291
  <ConnectionHoppingWarning {...warningProps} />
296
292
  <TerminalCmdSuggestions {...cmdSuggestionsProps} />
297
293
  <TransferQueue />
294
+ <AutoSync config={config} />
295
+ <AutoCheckUpdate config={config} />
298
296
  <WorkspaceSaveModal store={store} />
299
297
  <BookmarkFromHistoryModal />
300
298
  <NotificationContainer />
299
+ <BatchOpRunner />
301
300
  </div>
302
301
  </ConfigProvider>
303
302
  )
@@ -27,8 +27,6 @@
27
27
  border: 1px dashed var(--primary)
28
28
  .qm-item-dragover
29
29
  border-left 2px solid var(--primary)
30
- .qm-drag-handle
31
- cursor grab
32
30
  .qm-field-dragging
33
31
  opacity 0.4
34
32
  .qm-field-dragover
@@ -76,7 +76,7 @@ export default function renderQm (form) {
76
76
  onDrop={(e) => handleDrop(e, i, form)}
77
77
  onDragEnd={handleDragEnd}
78
78
  >
79
- <HolderOutlined className='mg1r qm-drag-handle' />
79
+ <HolderOutlined className='mg1r drag' />
80
80
 
81
81
  <Space.Addon>{e('delay')}</Space.Addon>
82
82
  <FormItem
@@ -51,6 +51,12 @@ export default class HotkeySetting extends Component {
51
51
  })
52
52
  }
53
53
 
54
+ handleClear = () => {
55
+ return this.props.onSaveConfig({
56
+ hotkey: ''
57
+ })
58
+ }
59
+
54
60
  convertToElectronAccelerator = (shortcut) => {
55
61
  if (!shortcut) return shortcut
56
62
 
@@ -117,7 +123,9 @@ export default class HotkeySetting extends Component {
117
123
  index: 0
118
124
  },
119
125
  updateConfig: this.onChangeHotkey,
120
- keysTaken: this.getKeysTaken(hotkey)
126
+ keysTaken: this.getKeysTaken(hotkey),
127
+ handleClear: this.handleClear,
128
+ renderClear: true
121
129
  }
122
130
 
123
131
  return (
@@ -11,7 +11,6 @@ import { noop } from 'lodash-es'
11
11
  import highlight from '../common/highlight'
12
12
  import { settingSyncId, settingCommonId, staticNewItemTabs } from '../../common/constants'
13
13
  import getInitItem from '../../common/init-setting-item'
14
- import './list.styl'
15
14
 
16
15
  const e = window.translate
17
16
 
@@ -8,6 +8,7 @@
8
8
  .list-item-remove
9
9
  .list-item-bookmark
10
10
  .list-item-duplicate
11
+ .list-item-autorun
11
12
  display none
12
13
  width 24px
13
14
  line-height 35px
@@ -15,6 +16,8 @@
15
16
  position absolute
16
17
  right 0
17
18
  top 0
19
+ .list-item-autorun
20
+ right 24px
18
21
 
19
22
  .list-item-title
20
23
  flex-grow: 1
@@ -37,6 +40,7 @@
37
40
  .list-item-remove
38
41
  .list-item-bookmark
39
42
  .list-item-duplicate
43
+ .list-item-autorun
40
44
  display block
41
45
  .theme-item:hover
42
46
  .list-item-remove
@@ -4,18 +4,22 @@
4
4
 
5
5
  import { auto } from 'manate/react'
6
6
  import { pick } from 'lodash-es'
7
- import { Tabs } from 'antd'
7
+ import { Tabs, Spin } from 'antd'
8
+ import { lazy, Suspense } from 'react'
8
9
  import SettingModal from './setting-wrap'
9
10
  import {
10
11
  settingMap,
11
12
  modals
12
13
  } from '../../common/constants'
13
- import TabBookmarks from './tab-bookmarks'
14
- import TabQuickCommands from './tab-quick-commands'
15
- import TabSettings from './tab-settings'
16
- import TabThemes from './tab-themes'
17
- import TabProfiles from './tab-profiles'
18
- import TabWidgets from './tab-widgets'
14
+
15
+ const TabBookmarks = lazy(() => import('./tab-bookmarks'))
16
+ const TabQuickCommands = lazy(() => import('./tab-quick-commands'))
17
+ const TabSettings = lazy(() => import('./tab-settings'))
18
+ const TabThemes = lazy(() => import('./tab-themes'))
19
+ const TabProfiles = lazy(() => import('./tab-profiles'))
20
+ const TabWidgets = lazy(() => import('./tab-widgets'))
21
+
22
+ const Loading = () => <div style={{ padding: 20, textAlign: 'center' }}><Spin /></div>
19
23
 
20
24
  const e = window.translate
21
25
 
@@ -115,46 +119,48 @@ export default auto(function SettingModalWrap (props) {
115
119
  <Tabs
116
120
  {...tabsProps}
117
121
  />
118
- <TabQuickCommands
119
- listProps={props0}
120
- settingItem={settingItem}
121
- formProps={formProps}
122
- store={store}
123
- settingTab={settingTab}
124
- />
125
- <TabBookmarks
126
- treeProps={treeProps}
127
- settingItem={settingItem}
128
- formProps={formProps}
129
- settingTab={settingTab}
130
- />
131
- <TabSettings
132
- listProps={props0}
133
- settingItem={settingItem}
134
- settingTab={settingTab}
135
- store={store}
136
- />
137
- <TabThemes
138
- listProps={props0}
139
- settingItem={settingItem}
140
- formProps={formProps}
141
- store={store}
142
- settingTab={settingTab}
143
- />
144
- <TabProfiles
145
- listProps={props0}
146
- settingItem={settingItem}
147
- formProps={formProps}
148
- store={store}
149
- settingTab={settingTab}
150
- />
151
- <TabWidgets
152
- listProps={props0}
153
- settingItem={settingItem}
154
- formProps={formProps}
155
- store={store}
156
- settingTab={settingTab}
157
- />
122
+ <Suspense fallback={<Loading />}>
123
+ <TabQuickCommands
124
+ listProps={props0}
125
+ settingItem={settingItem}
126
+ formProps={formProps}
127
+ store={store}
128
+ settingTab={settingTab}
129
+ />
130
+ <TabBookmarks
131
+ treeProps={treeProps}
132
+ settingItem={settingItem}
133
+ formProps={formProps}
134
+ settingTab={settingTab}
135
+ />
136
+ <TabSettings
137
+ listProps={props0}
138
+ settingItem={settingItem}
139
+ settingTab={settingTab}
140
+ store={store}
141
+ />
142
+ <TabThemes
143
+ listProps={props0}
144
+ settingItem={settingItem}
145
+ formProps={formProps}
146
+ store={store}
147
+ settingTab={settingTab}
148
+ />
149
+ <TabProfiles
150
+ listProps={props0}
151
+ settingItem={settingItem}
152
+ formProps={formProps}
153
+ store={store}
154
+ settingTab={settingTab}
155
+ />
156
+ <TabWidgets
157
+ listProps={props0}
158
+ settingItem={settingItem}
159
+ formProps={formProps}
160
+ store={store}
161
+ settingTab={settingTab}
162
+ />
163
+ </Suspense>
158
164
  </>
159
165
  )
160
166
  }
@@ -0,0 +1,53 @@
1
+ import { useEffect, useRef } from 'react'
2
+
3
+ export default function AutoSync ({ config }) {
4
+ const lastSyncTimeRef = useRef(0)
5
+ const intervalIdRef = useRef(null)
6
+
7
+ useEffect(() => {
8
+ if (
9
+ !config.syncSetting?.autoSync || config.syncSetting?.autoSyncInterval <= 0
10
+ ) {
11
+ clearInterval(intervalIdRef.current)
12
+ return
13
+ }
14
+ const checkAndSync = async () => {
15
+ const syncSetting = config.syncSetting || {}
16
+ const { autoSync, autoSyncInterval = 0, autoSyncDirection = 'upload' } = syncSetting
17
+
18
+ if (!autoSync) {
19
+ return
20
+ }
21
+
22
+ if (autoSyncInterval <= 0) {
23
+ return
24
+ }
25
+
26
+ const now = Date.now()
27
+ const intervalMs = autoSyncInterval * 60 * 1000
28
+ if (now - lastSyncTimeRef.current >= intervalMs) {
29
+ const { store } = window
30
+ if (autoSyncDirection === 'download') {
31
+ await store.downloadSettingAll()
32
+ } else {
33
+ await store.uploadSettingAll()
34
+ }
35
+ lastSyncTimeRef.current = now
36
+ }
37
+ }
38
+
39
+ intervalIdRef.current = setInterval(checkAndSync, 10000)
40
+
41
+ return () => {
42
+ if (intervalIdRef.current) {
43
+ clearInterval(intervalIdRef.current)
44
+ }
45
+ }
46
+ }, [
47
+ config.syncSetting?.autoSync,
48
+ config.syncSetting?.autoSyncInterval,
49
+ config.syncSetting?.autoSyncDirection
50
+ ])
51
+
52
+ return null
53
+ }
@@ -5,22 +5,65 @@
5
5
  import {
6
6
  Button,
7
7
  Switch,
8
- Tooltip
8
+ Select,
9
+ Space
9
10
  } from 'antd'
10
11
  import {
11
12
  ImportOutlined,
12
- ExportOutlined,
13
- InfoCircleOutlined
13
+ ExportOutlined
14
14
  } from '@ant-design/icons'
15
15
  import Upload from '../common/upload'
16
+ import HelpIcon from '../common/help-icon'
16
17
 
17
18
  const e = window.translate
18
19
 
20
+ const intervalOptions = [
21
+ { value: 0, label: e('autoSyncOnChange') },
22
+ { value: 5, label: '5 ' + e('minutes') },
23
+ { value: 10, label: '10 ' + e('minutes') },
24
+ { value: 15, label: '15 ' + e('minutes') },
25
+ { value: 30, label: '30 ' + e('minutes') },
26
+ { value: 60, label: '1 ' + e('hours') },
27
+ { value: 120, label: '2 ' + e('hours') },
28
+ { value: 360, label: '6 ' + e('hours') },
29
+ { value: 720, label: '12 ' + e('hours') },
30
+ { value: 1440, label: '24 ' + e('hours') }
31
+ ]
32
+
33
+ const directionOptions = [
34
+ { value: 'upload', label: e('uploadSettings') },
35
+ { value: 'download', label: e('downloadSettings') }
36
+ ]
37
+
19
38
  export default function DataTransport (props) {
20
39
  const txt = e('autoSync')
21
40
  const {
22
41
  store
23
42
  } = window
43
+
44
+ const syncSetting = props.config.syncSetting || {}
45
+ const autoSyncEnabled = syncSetting.autoSync || false
46
+ const autoSyncInterval = syncSetting.autoSyncInterval || 0
47
+ const autoSyncDirection = syncSetting.autoSyncDirection || 'upload'
48
+
49
+ function handleAutoSync (checked) {
50
+ store.updateSyncSetting({
51
+ autoSync: checked
52
+ })
53
+ }
54
+
55
+ function handleIntervalChange (value) {
56
+ store.updateSyncSetting({
57
+ autoSyncInterval: value
58
+ })
59
+ }
60
+
61
+ function handleDirectionChange (value) {
62
+ store.updateSyncSetting({
63
+ autoSyncDirection: value
64
+ })
65
+ }
66
+
24
67
  return (
25
68
  <div className='pd2 fix'>
26
69
  <div className='fleft'>
@@ -45,15 +88,33 @@ export default function DataTransport (props) {
45
88
  </div>
46
89
  <div className='fright'>
47
90
  <Switch
48
- checked={props.config.autoSync || false}
91
+ checked={autoSyncEnabled}
49
92
  checkedChildren={txt}
50
- onChange={store.handleAutoSync}
93
+ onChange={handleAutoSync}
51
94
  unCheckedChildren={txt}
52
95
  className='mg3l mg1r'
53
96
  />
54
- <Tooltip title={e('autoSyncTip')}>
55
- <InfoCircleOutlined />
56
- </Tooltip>
97
+ {autoSyncEnabled && (
98
+ <Space className='mg1l' size='small'>
99
+ <Select
100
+ value={autoSyncInterval}
101
+ onChange={handleIntervalChange}
102
+ options={intervalOptions}
103
+ style={{ width: 120 }}
104
+ popupMatchSelectWidth={false}
105
+ />
106
+ <Select
107
+ value={autoSyncDirection}
108
+ onChange={handleDirectionChange}
109
+ options={directionOptions}
110
+ style={{ width: 100 }}
111
+ popupMatchSelectWidth={false}
112
+ />
113
+ </Space>
114
+ )}
115
+ <HelpIcon
116
+ link='https://github.com/electerm/electerm/wiki/Auto-data-Sync'
117
+ />
57
118
  </div>
58
119
  </div>
59
120
  )
@@ -74,9 +74,15 @@ function renderAddonBefore (props, realPath) {
74
74
  }
75
75
 
76
76
  function renderAddonAfter (isLoadingRemote, onGoto, GoIcon, type) {
77
+ const handleClick = (e) => {
78
+ e.stopPropagation()
79
+ if (!isLoadingRemote) {
80
+ onGoto(type)
81
+ }
82
+ }
77
83
  return (
78
84
  <GoIcon
79
- onClick={isLoadingRemote ? () => null : () => onGoto(type)}
85
+ onClick={handleClick}
80
86
  />
81
87
  )
82
88
  }
@@ -135,11 +135,13 @@ export default class ShortcutEdit extends PureComponent {
135
135
  }
136
136
 
137
137
  renderClear () {
138
- if (this.props.renderClear && this.props.data.shortcut) {
138
+ const { renderClear, handleClear, data } = this.props
139
+ const hasShortcut = data && data.shortcut
140
+ if (renderClear && hasShortcut && handleClear) {
139
141
  return (
140
142
  <CloseOutlined
141
143
  className='pointer mg1l'
142
- onClick={this.props.handleClear}
144
+ onClick={handleClear}
143
145
  />
144
146
  )
145
147
  }
@@ -6,7 +6,7 @@ import { auto } from 'manate/react'
6
6
  import TreeList from '../tree-list/tree-list'
7
7
 
8
8
  export default auto(function BookmarkSelect (props) {
9
- const { store, from } = props
9
+ const { store, from, autoFocus } = props
10
10
  const {
11
11
  listStyle,
12
12
  openedSideBar,
@@ -38,7 +38,8 @@ export default auto(function BookmarkSelect (props) {
38
38
  bookmarkGroups: store.getBookmarkGroupsTotal(),
39
39
  expandedKeys,
40
40
  leftSidebarWidth,
41
- bookmarkGroupTree: store.bookmarkGroupTree
41
+ bookmarkGroupTree: store.bookmarkGroupTree,
42
+ autoFocus
42
43
  }
43
44
  return (
44
45
  <TreeList
@@ -40,7 +40,9 @@ export default function HistoryItem (props) {
40
40
  e.stopPropagation()
41
41
  refsStatic.get('bookmark-from-history-modal')?.show(item.tab)
42
42
  }
43
-
43
+ if (!item.tab) {
44
+ return null
45
+ }
44
46
  const title = createTitleWithTag(item.tab)
45
47
  const tt = createTitle(item.tab)
46
48
  return (
@@ -8,6 +8,7 @@ import { Switch } from 'antd'
8
8
  import { UnorderedListOutlined } from '@ant-design/icons'
9
9
  import HistoryItem from './history-item'
10
10
  import { getItemJSON, setItemJSON } from '../../common/safe-local-storage.js'
11
+ import '../setting-panel/list.styl'
11
12
 
12
13
  const SORT_BY_FREQ_KEY = 'electerm-history-sort-by-frequency'
13
14
 
@@ -6,7 +6,6 @@ import {
6
6
  PlusCircleOutlined,
7
7
  SettingOutlined,
8
8
  UpCircleOutlined,
9
- BarsOutlined,
10
9
  AppstoreOutlined,
11
10
  ThunderboltOutlined
12
11
  } from '@ant-design/icons'
@@ -92,7 +91,6 @@ export default function Sidebar (props) {
92
91
  openAbout,
93
92
  openSettingSync,
94
93
  openTerminalThemes,
95
- toggleBatchOp,
96
94
  setLeftSidePanelWidth
97
95
  } = store
98
96
  const {
@@ -102,7 +100,6 @@ export default function Sidebar (props) {
102
100
  shouldUpgrade
103
101
  } = upgradeInfo
104
102
  const showSetting = showModal === modals.setting
105
- const showBatchOp = showModal === modals.batchOps
106
103
  const settingActive = showSetting && settingTab === settingMap.setting && settingItem.id === 'setting-common'
107
104
  const syncActive = showSetting && settingTab === settingMap.setting && settingItem.id === 'setting-sync'
108
105
  const themeActive = showSetting && settingTab === settingMap.terminalThemes
@@ -190,12 +187,6 @@ export default function Sidebar (props) {
190
187
  spin={isSyncingSetting}
191
188
  />
192
189
  </SideIcon>
193
- <SideIcon
194
- title={e('batchOp')}
195
- active={showBatchOp}
196
- >
197
- <BarsOutlined className='iblock font20 control-icon' onClick={toggleBatchOp} />
198
- </SideIcon>
199
190
  <SideIcon
200
191
  title={e('widgets')}
201
192
  active={widgetsActive}
@@ -75,7 +75,7 @@ export default function AddBtnMenu ({
75
75
 
76
76
  let listContent
77
77
  if (activeTab === 'bookmarks') {
78
- listContent = <BookmarksList store={window.store} />
78
+ listContent = <BookmarksList store={window.store} autoFocus />
79
79
  } else {
80
80
  listContent = <History store={window.store} />
81
81
  }
@@ -40,15 +40,18 @@ export default class AddBtn extends Component {
40
40
  componentWillUnmount () {
41
41
  if (this.state.open) {
42
42
  document.removeEventListener('click', this.handleDocumentClick)
43
+ document.removeEventListener('keydown', this.handleKeyDown)
43
44
  }
44
45
  // Clean up portal container
45
46
  if (this.portalContainer) {
46
47
  document.body.removeChild(this.portalContainer)
47
48
  this.portalContainer = null
48
49
  }
49
- // Clear focus timeout
50
- if (this.çƒ) {
51
- clearTimeout(this.focusTimeout)
50
+ }
51
+
52
+ handleKeyDown = (e) => {
53
+ if (e.key === 'Escape') {
54
+ this.setState({ open: false })
52
55
  }
53
56
  }
54
57
 
@@ -56,8 +59,10 @@ export default class AddBtn extends Component {
56
59
  // Attach or detach document click listener only when menu open state changes
57
60
  if (this.state.open && !prevState.open) {
58
61
  document.addEventListener('click', this.handleDocumentClick)
62
+ document.addEventListener('keydown', this.handleKeyDown)
59
63
  } else if (!this.state.open && prevState.open) {
60
64
  document.removeEventListener('click', this.handleDocumentClick)
65
+ document.removeEventListener('keydown', this.handleKeyDown)
61
66
  }
62
67
  }
63
68
 
@@ -115,17 +120,6 @@ export default class AddBtn extends Component {
115
120
  )
116
121
  }
117
122
 
118
- focusSearchInput = () => {
119
- // Focus the search input after the menu renders
120
- this.focusTimeout = setTimeout(() => {
121
- const searchInput = this.menuRef.current?.querySelector('.add-menu-list .ant-input')
122
- if (searchInput) {
123
- searchInput.focus()
124
- searchInput.select()
125
- }
126
- }, 500)
127
- }
128
-
129
123
  handleAddBtnClick = () => {
130
124
  if (this.state.open) {
131
125
  this.setState({ open: false })
@@ -172,7 +166,7 @@ export default class AddBtn extends Component {
172
166
  menuPosition,
173
167
  menuTop,
174
168
  menuLeft
175
- }, this.focusSearchInput)
169
+ })
176
170
 
177
171
  window.openTabBatch = this.props.batch
178
172
  }
@@ -1,5 +1,5 @@
1
1
  import { useState, useRef, useEffect } from 'react'
2
- import { Button, Space } from 'antd'
2
+ import { Button, Space, Input } from 'antd'
3
3
  import { ArrowRightOutlined, ThunderboltOutlined } from '@ant-design/icons'
4
4
  import message from '../common/message'
5
5
  import InputAutoFocus from '../common/input-auto-focus'
@@ -29,18 +29,14 @@ export default function QuickConnect ({ batch, inputOnly }) {
29
29
  const [inputValue, setInputValue] = useState('')
30
30
  const inputRef = useRef(null)
31
31
 
32
- useEffect(() => {
33
- if (showInput && inputRef.current) {
34
- inputRef.current.focus()
35
- }
36
- }, [showInput])
37
-
38
- // When inputOnly is true, always show the input
32
+ // When inputOnly is true, always show the input (without auto-focus)
39
33
  useEffect(() => {
40
34
  if (inputOnly) {
41
35
  setShowInput(true)
36
+ } else if (showInput && inputRef.current) {
37
+ inputRef.current.focus()
42
38
  }
43
- }, [inputOnly])
39
+ }, [inputOnly, showInput])
44
40
 
45
41
  const handleToggle = () => {
46
42
  setShowInput(!showInput)
@@ -94,7 +90,7 @@ export default function QuickConnect ({ batch, inputOnly }) {
94
90
  <Button
95
91
  {...iconsProps1}
96
92
  />
97
- <InputAutoFocus {...inputProps} />
93
+ {inputOnly ? <Input {...inputProps} /> : <InputAutoFocus {...inputProps} />}
98
94
  <Button
99
95
  {...iconProps}
100
96
  />