@electerm/electerm-react 1.72.48 → 1.80.2

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 (48) hide show
  1. package/client/common/constants.js +1 -1
  2. package/client/common/sftp.js +3 -1
  3. package/client/components/ai/ai-config.jsx +7 -1
  4. package/client/components/batch-op/batch-op.jsx +4 -5
  5. package/client/components/bg/css-overwrite.jsx +179 -0
  6. package/client/components/bg/shapes.js +501 -0
  7. package/client/components/bookmark-form/form-tabs.jsx +1 -0
  8. package/client/components/bookmark-form/local-form-ui.jsx +7 -1
  9. package/client/components/bookmark-form/render-bg.jsx +43 -0
  10. package/client/components/bookmark-form/serial-form-ui.jsx +7 -1
  11. package/client/components/bookmark-form/ssh-form-ui.jsx +14 -3
  12. package/client/components/bookmark-form/telnet-form-ui.jsx +7 -1
  13. package/client/components/bookmark-form/use-ui.jsx +68 -72
  14. package/client/components/common/ref.js +2 -0
  15. package/client/components/{sftp/confirm-modal-store.jsx → file-transfer/conflict-resolve.jsx} +86 -48
  16. package/client/components/file-transfer/transfer-queue.jsx +151 -0
  17. package/client/components/file-transfer/transfer.jsx +582 -0
  18. package/client/components/{sftp → file-transfer}/transports-action-store.jsx +35 -32
  19. package/client/components/{sftp → file-transfer}/transports-ui-store.jsx +2 -2
  20. package/client/components/main/main.jsx +25 -18
  21. package/client/components/main/wrapper.styl +16 -0
  22. package/client/components/profile/profile-list.jsx +1 -1
  23. package/client/components/quick-commands/qm.styl +4 -1
  24. package/client/components/quick-commands/quick-commands-list.jsx +16 -4
  25. package/client/components/setting-panel/list.jsx +1 -1
  26. package/client/components/setting-panel/setting-terminal.jsx +2 -1
  27. package/client/components/setting-panel/terminal-bg-config.jsx +15 -2
  28. package/client/components/sftp/file-info-modal.jsx +3 -0
  29. package/client/components/sftp/file-item.jsx +25 -23
  30. package/client/components/sftp/file-read.js +1 -27
  31. package/client/components/sftp/list-table-ui.jsx +2 -2
  32. package/client/components/sidebar/transfer-history-modal.jsx +1 -1
  33. package/client/components/sidebar/transfer-list-control.jsx +1 -1
  34. package/client/components/sidebar/transport-ui.jsx +16 -9
  35. package/client/components/terminal/terminal.jsx +23 -1
  36. package/client/components/text-editor/simple-editor.jsx +164 -0
  37. package/client/components/text-editor/text-editor-form.jsx +6 -9
  38. package/client/css/includes/box.styl +2 -2
  39. package/client/store/tab.js +5 -1
  40. package/client/store/transfer-list.js +10 -51
  41. package/package.json +1 -1
  42. package/client/components/main/css-overwrite.jsx +0 -91
  43. package/client/components/sftp/transfer-conflict-store.jsx +0 -284
  44. package/client/components/sftp/transport-action-store.jsx +0 -422
  45. package/client/components/sftp/zip.js +0 -42
  46. /package/client/components/{main → bg}/custom-css.jsx +0 -0
  47. /package/client/components/{sftp → file-transfer}/transfer-speed-format.js +0 -0
  48. /package/client/components/{sftp → file-transfer}/transfer.styl +0 -0
@@ -26,76 +26,72 @@ export default function useBookmarkFormUI (props) {
26
26
  fontFamily: defaultFontFamily,
27
27
  fontSize: defaultFontSize
28
28
  } = defaultSettings
29
- return [
30
- <FormItem
31
- {...formItemLayout}
32
- label='ENV:LANG'
33
- key='envLang'
34
- rules={[{
35
- max: 130, message: '130 chars max'
36
- }]}
37
- name='envLang'
38
- >
39
- <Input placeholder={defaultEnvLang} />
40
- </FormItem>,
41
- <FormItem
42
- {...formItemLayout}
43
- key='terminalType'
44
- label={e('terminalType')}
45
- rules={[{
46
- required: true, message: 'terminal type required'
47
- }]}
48
- normalize={props.trim}
49
- name='term'
50
- >
51
- <AutoComplete
52
- options={terminalTypes.map(mapper)}
53
- />
54
- </FormItem>,
55
- <FormItem
56
- {...formItemLayout}
57
- label={e('displayRaw')}
58
- name='displayRaw'
59
- key='displayRaw'
60
- valuePropName='checked'
61
- >
62
- <Switch />
63
- </FormItem>,
64
- <FormItem
65
- {...formItemLayout}
66
- label={e('fontFamily')}
67
- key='fontFamily'
68
- name='fontFamily'
69
- rules={[{
70
- max: 130, message: '130 chars max'
71
- }]}
72
- >
73
- <Input placeholder={defaultFontFamily + ''} />
74
- </FormItem>,
75
- <FormItem
76
- key='fontSize'
77
- {...formItemLayout}
78
- label={e('fontSize')}
79
- name='fontSize'
80
- >
81
- <InputNumber
82
- min={9}
83
- max={65535}
84
- step={1}
85
- placeholder={defaultFontSize}
86
- />
87
- </FormItem>,
88
- <FormItem
89
- key='keepaliveInterval'
90
- {...formItemLayout}
91
- label={e('keepaliveIntervalDesc')}
92
- name='keepaliveInterval'
93
- >
94
- <InputNumber
95
- min={0}
96
- max={20000000}
97
- step={1000}
98
- />
99
- </FormItem>
100
- ]
29
+ return (
30
+ <>
31
+ <FormItem
32
+ {...formItemLayout}
33
+ label='ENV:LANG'
34
+ rules={[{
35
+ max: 130, message: '130 chars max'
36
+ }]}
37
+ name='envLang'
38
+ >
39
+ <Input placeholder={defaultEnvLang} />
40
+ </FormItem>,
41
+ <FormItem
42
+ {...formItemLayout}
43
+ label={e('terminalType')}
44
+ rules={[{
45
+ required: true, message: 'terminal type required'
46
+ }]}
47
+ normalize={props.trim}
48
+ name='term'
49
+ >
50
+ <AutoComplete
51
+ options={terminalTypes.map(mapper)}
52
+ />
53
+ </FormItem>,
54
+ <FormItem
55
+ {...formItemLayout}
56
+ label={e('displayRaw')}
57
+ name='displayRaw'
58
+ valuePropName='checked'
59
+ >
60
+ <Switch />
61
+ </FormItem>,
62
+ <FormItem
63
+ {...formItemLayout}
64
+ label={e('fontFamily')}
65
+ name='fontFamily'
66
+ rules={[{
67
+ max: 130, message: '130 chars max'
68
+ }]}
69
+ >
70
+ <Input placeholder={defaultFontFamily + ''} />
71
+ </FormItem>,
72
+ <FormItem
73
+ {...formItemLayout}
74
+ label={e('fontSize')}
75
+ name='fontSize'
76
+ >
77
+ <InputNumber
78
+ min={9}
79
+ max={65535}
80
+ step={1}
81
+ placeholder={defaultFontSize}
82
+ />
83
+ </FormItem>,
84
+ <FormItem
85
+ {...formItemLayout}
86
+ label={e('keepaliveIntervalDesc')}
87
+ name='keepaliveInterval'
88
+ >
89
+ <InputNumber
90
+ min={0}
91
+ max={20000000}
92
+ step={1000}
93
+ />
94
+ </FormItem>
95
+ </>
96
+ )
101
97
  }
@@ -1,6 +1,7 @@
1
1
  // components instance reference holder
2
2
  window.refs = new Map()
3
3
  window.refsStatic = new Map()
4
+ window.refsTransfers = new Map()
4
5
 
5
6
  class Ref {
6
7
  constructor (key) {
@@ -21,4 +22,5 @@ class Ref {
21
22
  }
22
23
 
23
24
  export const refs = new Ref('refs')
25
+ export const refsTransfers = new Ref('refsTransfers')
24
26
  export const refsStatic = new Ref('refsStatic')
@@ -3,6 +3,7 @@
3
3
  *
4
4
  */
5
5
 
6
+ import { Component } from 'react'
6
7
  import { Modal, Button } from 'antd'
7
8
  import { isString } from 'lodash-es'
8
9
  import AnimateText from '../common/animate-text'
@@ -11,8 +12,7 @@ import { FolderOutlined, FileOutlined } from '@ant-design/icons'
11
12
  import {
12
13
  fileActions
13
14
  } from '../../common/constants'
14
- import deepCopy from 'json-deep-copy'
15
- import { refsStatic } from '../common/ref'
15
+ import { refsStatic, refsTransfers } from '../common/ref'
16
16
 
17
17
  const e = window.translate
18
18
 
@@ -23,30 +23,66 @@ function formatTimeAuto (strOrDigit) {
23
23
  return formatTime(strOrDigit * 1000)
24
24
  }
25
25
 
26
- export default function ConfirmModalStore (props) {
27
- function act (action) {
28
- const { transferToConfirm } = props
29
- window.store.transferToConfirm = {}
30
- const {
31
- fromFile: {
32
- id: fileId
33
- },
34
- id,
35
- transferGroupId
36
- } = transferToConfirm
37
- refsStatic.get('transfer-conflict')?.onDecision({
38
- transferGroupId,
39
- fileId,
40
- id,
41
- transfer: deepCopy(transferToConfirm),
42
- action
26
+ export default class ConfirmModalStore extends Component {
27
+ constructor (props) {
28
+ super(props)
29
+ this.state = {
30
+ transferToConfirm: null
31
+ }
32
+ this.queue = []
33
+ this.id = 'transfer-conflict'
34
+ refsStatic.add(this.id, this)
35
+ }
36
+
37
+ addConflict = (transfer) => {
38
+ this.queue.push(transfer)
39
+ if (!this.state.transferToConfirm) {
40
+ this.showNext()
41
+ }
42
+ }
43
+
44
+ showNext = () => {
45
+ const next = this.queue.shift()
46
+ this.setState({
47
+ transferToConfirm: next
43
48
  })
44
49
  }
45
50
 
46
- function renderContent () {
51
+ act = (action) => {
52
+ const { id, transferBatch } = this.state.transferToConfirm
53
+ const toAll = action.includes('All')
54
+ const policy = toAll ? action.replace('All', '') : action
55
+ const trid = `tr-${transferBatch}-${id}`
56
+ const doFilter = toAll && transferBatch
57
+
58
+ // For "All" actions, update all existing transfers in the same batch
59
+ if (doFilter) {
60
+ // Update all existing transfers with same batch ID in DOM
61
+ const prefix = `tr-${transferBatch}-`
62
+ for (const [key, r] of window.refsTransfers.entries()) {
63
+ if (key.startsWith(prefix)) {
64
+ if (key !== trid) {
65
+ r.resolvePolicy = policy
66
+ r.onDecision(policy)
67
+ }
68
+ }
69
+ }
70
+ this.queue = this.queue.filter(d => d.transferBatch !== transferBatch)
71
+ }
72
+
73
+ // Resolve current conflict
74
+ refsTransfers.get(trid)?.onDecision(policy)
75
+
76
+ // Move to the next item
77
+ this.setState({
78
+ transferToConfirm: null
79
+ }, this.showNext)
80
+ }
81
+
82
+ renderContent () {
47
83
  const {
48
84
  transferToConfirm
49
- } = props
85
+ } = this.state
50
86
  const {
51
87
  fromPath,
52
88
  toPath,
@@ -100,10 +136,10 @@ export default function ConfirmModalStore (props) {
100
136
  )
101
137
  }
102
138
 
103
- function renderFooter () {
139
+ renderFooter () {
104
140
  const {
105
141
  transferToConfirm
106
- } = props
142
+ } = this.state
107
143
  if (!transferToConfirm) {
108
144
  return null
109
145
  }
@@ -117,14 +153,14 @@ export default function ConfirmModalStore (props) {
117
153
  <Button
118
154
  type='dashed'
119
155
  className='mg1l'
120
- onClick={() => act(fileActions.cancel)}
156
+ onClick={() => this.act(fileActions.cancel)}
121
157
  >
122
158
  {e('cancel')}
123
159
  </Button>
124
160
  <Button
125
161
  type='dashed'
126
162
  className='mg1l'
127
- onClick={() => act(fileActions.skip)}
163
+ onClick={() => this.act(fileActions.skip)}
128
164
  >
129
165
  {e('skip')}
130
166
  </Button>
@@ -132,7 +168,7 @@ export default function ConfirmModalStore (props) {
132
168
  danger
133
169
  className='mg1l'
134
170
  onClick={
135
- () => act(fileActions.mergeOrOverwrite)
171
+ () => this.act(fileActions.mergeOrOverwrite)
136
172
  }
137
173
  >
138
174
  {isDirectory ? e('merge') : e('overwrite')}
@@ -141,7 +177,7 @@ export default function ConfirmModalStore (props) {
141
177
  type='primary'
142
178
  className='mg1l'
143
179
  onClick={
144
- () => act(fileActions.rename)
180
+ () => this.act(fileActions.rename)
145
181
  }
146
182
  >
147
183
  {e('rename')}
@@ -157,7 +193,7 @@ export default function ConfirmModalStore (props) {
157
193
  : e('overwriteDesc')
158
194
  }
159
195
  onClick={
160
- () => act(fileActions.mergeOrOverwriteAll)
196
+ () => this.act(fileActions.mergeOrOverwriteAll)
161
197
  }
162
198
  >
163
199
  {isDirectory ? e('mergeAll') : e('overwriteAll')}
@@ -167,7 +203,7 @@ export default function ConfirmModalStore (props) {
167
203
  className='mg1l'
168
204
  title={e('renameDesc')}
169
205
  onClick={
170
- () => act(fileActions.renameAll)
206
+ () => this.act(fileActions.renameAll)
171
207
  }
172
208
  >
173
209
  {e('renameAll')}
@@ -177,24 +213,26 @@ export default function ConfirmModalStore (props) {
177
213
  )
178
214
  }
179
215
 
180
- const {
181
- transferToConfirm
182
- } = props
183
- if (!transferToConfirm.id) {
184
- return null
185
- }
186
- const modalProps = {
187
- open: true,
188
- width: 500,
189
- title: e('fileConflict'),
190
- footer: renderFooter(),
191
- onCancel: () => act(fileActions.cancel)
216
+ render () {
217
+ const {
218
+ transferToConfirm
219
+ } = this.state
220
+ if (!transferToConfirm?.id) {
221
+ return null
222
+ }
223
+ const modalProps = {
224
+ open: true,
225
+ width: 500,
226
+ title: e('fileConflict'),
227
+ footer: this.renderFooter(),
228
+ onCancel: () => this.act(fileActions.cancel)
229
+ }
230
+ return (
231
+ <Modal
232
+ {...modalProps}
233
+ >
234
+ {this.renderContent()}
235
+ </Modal>
236
+ )
192
237
  }
193
- return (
194
- <Modal
195
- {...modalProps}
196
- >
197
- {renderContent()}
198
- </Modal>
199
- )
200
238
  }
@@ -0,0 +1,151 @@
1
+ import { Component } from 'react'
2
+ import { autoRun, action } from 'manate'
3
+ import { refsStatic } from '../common/ref'
4
+
5
+ export default class Queue extends Component {
6
+ constructor (props) {
7
+ super(props)
8
+ this.queue = []
9
+ this.currentRun = null
10
+ this.id = 'transfer-queue'
11
+ refsStatic.add(this.id, this)
12
+ }
13
+
14
+ componentWillUnmount () {
15
+ if (this.currentRun) {
16
+ this.currentRun.stop()
17
+ }
18
+ }
19
+
20
+ addToQueue = (operation, ...args) => {
21
+ this.queue.push({ operation, args })
22
+ this.processQueue()
23
+ }
24
+
25
+ processQueue = async () => {
26
+ if (this.queue.length === 0 || this.processing === true) {
27
+ return
28
+ }
29
+ this.processing = true
30
+ const { operation, args } = this.queue.shift()
31
+
32
+ await this.executeOperation(operation, ...args)
33
+ .catch((error) => {
34
+ console.error('Error processing operation:', error)
35
+ })
36
+ this.processing = false
37
+ this.processQueue()
38
+ }
39
+
40
+ executeOperation = (operation, ...args) => {
41
+ return new Promise((resolve, reject) => {
42
+ const { fileTransfers } = window.store
43
+ const [id, updateObj] = args
44
+ const end = () => {
45
+ this.currentRun && this.currentRun.stop()
46
+ resolve()
47
+ }
48
+
49
+ // Determine if this is a simple operation (same-side cp) vs a file transfer
50
+ const isTransferInit = updateObj && updateObj.inited === true
51
+
52
+ // For file transfers, set up autoRun before modifying data
53
+ if (isTransferInit) {
54
+ this.currentRun = autoRun(() => {
55
+ checkCompletion()
56
+ })
57
+ this.currentRun.start()
58
+
59
+ // Use setTimeout for transfer operations to give autoRun time to initialize
60
+ setTimeout(() => {
61
+ applyChanges()
62
+ }, 1)
63
+ } else {
64
+ // For same-side operations, just apply changes immediately
65
+ applyChanges()
66
+
67
+ // Still set up autoRun as a safety net, but we won't need it usually
68
+ this.currentRun = autoRun(() => {
69
+ checkCompletion()
70
+ })
71
+ this.currentRun.start()
72
+ }
73
+
74
+ function applyChanges () {
75
+ if (operation === 'update') {
76
+ const index = fileTransfers.findIndex(t => t.id === id)
77
+ if (index < 0) {
78
+ return end()
79
+ }
80
+ Object.assign(fileTransfers[index], updateObj)
81
+ } else if (operation === 'delete') {
82
+ const index = fileTransfers.findIndex(t => t.id === id)
83
+ if (index < 0) {
84
+ return end()
85
+ }
86
+ fileTransfers.splice(index, 1)
87
+ } else if (operation === 'insert') {
88
+ fileTransfers.push(id)
89
+ } else if (operation === 'moveTop') {
90
+ // New moveTop operation - wrapped with action
91
+ action(() => {
92
+ const index = fileTransfers.findIndex(t => t.id === id)
93
+ if (index < 0 || index === 0) {
94
+ return end() // Already at top or doesn't exist
95
+ }
96
+ const transfer = fileTransfers[index]
97
+ fileTransfers.splice(index, 1) // Remove from current position
98
+ fileTransfers.unshift(transfer) // Add to the beginning
99
+ })()
100
+ }
101
+
102
+ // For non-transfer operations, check immediately
103
+ // if (!isTransferInit) {
104
+ // checkCompletion()
105
+ // }
106
+ }
107
+
108
+ function checkCompletion () {
109
+ if (operation === 'update') {
110
+ const index = window.store.fileTransfers.findIndex(t => t.id === id)
111
+ const updatedTransfer = index >= 0 ? window.store.fileTransfers[index] : null
112
+
113
+ if (updatedTransfer && updateObj) {
114
+ const allUpdated = Object.keys(updateObj).every(key => {
115
+ const updatedValue = updatedTransfer[key]
116
+ const expectedValue = updateObj[key]
117
+
118
+ if (expectedValue && typeof expectedValue === 'object') {
119
+ return updatedValue !== undefined
120
+ }
121
+ return updatedValue === expectedValue
122
+ })
123
+
124
+ if (allUpdated) {
125
+ end()
126
+ }
127
+ }
128
+ } else if (operation === 'delete') {
129
+ const stillExists = window.store.fileTransfers.some(t => t.id === id)
130
+ if (!stillExists) {
131
+ end()
132
+ }
133
+ } else if (operation === 'insert') {
134
+ const exists = window.store.fileTransfers.some(t => t.id === id)
135
+ if (exists) {
136
+ end()
137
+ }
138
+ } else if (operation === 'moveTop') {
139
+ const index = window.store.fileTransfers.findIndex(t => t.id === id)
140
+ if (index === 0) {
141
+ end()
142
+ }
143
+ }
144
+ }
145
+ })
146
+ }
147
+
148
+ render () {
149
+ return null // This component doesn't render anything
150
+ }
151
+ }