@electerm/electerm-react 1.38.60 → 1.38.70

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 (36) hide show
  1. package/client/common/constants.js +3 -2
  2. package/client/common/create-title.jsx +9 -1
  3. package/client/common/sftp.js +3 -0
  4. package/client/components/batch-op/batch-op.jsx +1 -6
  5. package/client/components/bookmark-form/bookmark-form.styl +3 -1
  6. package/client/components/bookmark-form/render-ssh-tunnel.jsx +210 -88
  7. package/client/components/bookmark-form/ssh-form-ui.jsx +1 -1
  8. package/client/components/main/main.jsx +14 -0
  9. package/client/components/quick-commands/quick-commands-box.jsx +5 -4
  10. package/client/components/sftp/{confirm-modal.jsx → confirm-modal-store.jsx} +81 -50
  11. package/client/components/sftp/file-item.jsx +2 -0
  12. package/client/components/sftp/sftp-entry.jsx +34 -40
  13. package/client/components/sftp/transfer-conflict-store.jsx +291 -0
  14. package/client/components/sftp/transport-action-store.jsx +430 -0
  15. package/client/components/sftp/transports-action-store.jsx +102 -0
  16. package/client/components/sftp/transports-ui-store.jsx +30 -0
  17. package/client/components/sidebar/transfer-list-control.jsx +5 -14
  18. package/client/components/sidebar/transport-ui.jsx +2 -12
  19. package/client/components/tabs/tab.jsx +43 -2
  20. package/client/components/tabs/tabs.styl +1 -1
  21. package/client/components/terminal/index.jsx +1 -0
  22. package/client/components/terminal/terminal-interactive.jsx +15 -0
  23. package/client/components/terminal-info/disk.jsx +9 -0
  24. package/client/store/index.js +4 -0
  25. package/client/store/init-state.js +2 -3
  26. package/client/store/sync.js +5 -2
  27. package/client/store/tab.js +1 -1
  28. package/client/store/transfer-list.js +55 -2
  29. package/client/store/watch.js +0 -8
  30. package/package.json +1 -1
  31. package/client/components/sftp/transfer-conflict.jsx +0 -323
  32. package/client/components/sftp/transport-action.jsx +0 -412
  33. package/client/components/sftp/transport-entry.jsx +0 -108
  34. package/client/components/sftp/transport-types.js +0 -8
  35. package/client/components/sftp/transports-action.jsx +0 -111
  36. package/client/components/sftp/transports-ui.jsx +0 -93
@@ -38,7 +38,7 @@ export const contextMenuPaddingTop = 10
38
38
  export const sftpControlHeight = 28 + 42 + 33 + 36
39
39
  export const sidebarWidth = 43
40
40
  export const maxHistory = 50
41
- export const maxTransport = 5
41
+ export const maxTransport = 1
42
42
  export const maxSftpHistory = 20
43
43
  export const maxZoom = 8
44
44
  export const minZoom = 0.5
@@ -278,7 +278,8 @@ export const commonActions = {
278
278
  closeContextMenu: 'close-context-menu',
279
279
  clickContextMenu: 'click-context-menu',
280
280
  openContextMenu: 'open-context-menu',
281
- addTransfer: 'add-transfer'
281
+ addTransfer: 'add-transfer',
282
+ sftpList: 'sftp-list'
282
283
  }
283
284
 
284
285
  export const srcsSkipUpgradeCheck = [
@@ -15,7 +15,7 @@ export default function createTitle (res) {
15
15
  }
16
16
  const {
17
17
  host, port, username, title, type,
18
- path, connectionHoppings
18
+ path, connectionHoppings, sshTunnels
19
19
  } = res
20
20
  const fixTitle = `${username || ''}@${host}:${port}`
21
21
  const extra = host || path ? (path || fixTitle) : ''
@@ -25,6 +25,14 @@ export default function createTitle (res) {
25
25
  if (connectionHoppings && connectionHoppings.length) {
26
26
  f = `[⋙]${f}`
27
27
  }
28
+ if (
29
+ sshTunnels &&
30
+ sshTunnels.length &&
31
+ sshTunnels[0].sshTunnel &&
32
+ sshTunnels[0].sshTunnelRemoteHost
33
+ ) {
34
+ f = `[T]${f}`
35
+ }
28
36
  if (type) {
29
37
  f = `[${type}]${f}`
30
38
  }
@@ -7,6 +7,7 @@ import Transfer from './transfer'
7
7
  import { transferTypeMap, instSftpKeys as keys } from './constants'
8
8
  import initWs from './ws'
9
9
 
10
+ window.sftps = {}
10
11
  const transferKeys = Object.keys(transferTypeMap)
11
12
 
12
13
  class Sftp {
@@ -56,6 +57,7 @@ class Sftp {
56
57
  }
57
58
 
58
59
  async destroy () {
60
+ delete window.sftps[this.sessionId]
59
61
  const { ws } = this
60
62
  ws.s({
61
63
  action: 'sftp-destroy',
@@ -69,5 +71,6 @@ class Sftp {
69
71
  export default async (sessionId) => {
70
72
  const sftp = new Sftp()
71
73
  await sftp.init(sessionId)
74
+ window.sftps[sessionId] = sftp
72
75
  return sftp
73
76
  }
@@ -26,7 +26,6 @@ import { autoRun } from 'manate'
26
26
  import { pick } from 'lodash-es'
27
27
  import { runCmd } from '../terminal/terminal-apis'
28
28
  import deepCopy from 'json-deep-copy'
29
- import postMsg from '../../common/post-msg'
30
29
  import uid from '../../common/uid'
31
30
  import wait from '../../common/wait'
32
31
  import { getFolderFromFilePath } from '../sftp/file-read'
@@ -186,11 +185,7 @@ export default class BatchOp extends Component {
186
185
  zip: true,
187
186
  skipConfirm: true
188
187
  }
189
- postMsg({
190
- list: [obj],
191
- action: commonActions.addTransfer,
192
- sessionId: tab.sessionId
193
- })
188
+ window.store.addTransferList([obj])
194
189
  const { store } = window
195
190
  this.tm = setTimeout(() => {
196
191
  reject(new Error('timeout'))
@@ -5,4 +5,6 @@
5
5
  .mg60b
6
6
  margin-bottom 60px
7
7
  .compact-input input
8
- width 80px !important
8
+ width 80px !important
9
+ .ssh-tunnels-host
10
+ margin-bottom 0
@@ -5,30 +5,154 @@ import {
5
5
  Radio,
6
6
  Space,
7
7
  Button,
8
- Tooltip
8
+ Tooltip,
9
+ Table
9
10
  } from 'antd'
10
- import { MinusCircleOutlined, PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons'
11
- import { formItemLayout } from '../../common/form-layout'
11
+ import { useState } from 'react'
12
+ import { PlusOutlined, QuestionCircleOutlined, MinusCircleFilled } from '@ant-design/icons'
13
+ import { formItemLayout, tailFormItemLayout } from '../../common/form-layout'
14
+ import uid from '../../common/uid'
12
15
 
13
16
  const FormItem = Form.Item
14
- const FormList = Form.List
15
17
  const {
16
18
  Button: RadioButton,
17
19
  Group: RadioGroup
18
20
  } = Radio
19
21
  const { prefix } = window
20
22
  const e = prefix('ssh')
23
+ const s = prefix('sftp')
24
+ const m = prefix('menu')
21
25
 
22
- export default function renderSshTunnel () {
23
- function renderItem (field, i, add, remove) {
26
+ export default function renderSshTunnels (props) {
27
+ const {
28
+ form,
29
+ formData
30
+ } = props
31
+ const [formChild] = Form.useForm()
32
+ const [initialValues] = useState({
33
+ sshTunnelLocalPort: 12200,
34
+ sshTunnelLocalHost: '127.0.0.1',
35
+ sshTunnelRemotePort: 12300,
36
+ sshTunnelRemoteHost: '127.0.0.1'
37
+ })
38
+ const [list, setList] = useState(formData.sshTunnels || [])
39
+ function onSubmit () {
40
+ formChild.submit()
41
+ }
42
+ function handleFinish (data) {
43
+ const nd = {
44
+ ...data,
45
+ id: uid()
46
+ }
47
+ const v = [
48
+ ...form.getFieldValue('sshTunnels'),
49
+ nd
50
+ ]
51
+ form.setFieldsValue({
52
+ sshTunnels: v
53
+ })
54
+ setList(old => {
55
+ return [
56
+ ...old,
57
+ data
58
+ ]
59
+ })
60
+ formChild.resetFields()
61
+ }
62
+
63
+ function remove (id) {
64
+ setList(old => {
65
+ return old.filter(i => i.id !== id)
66
+ })
67
+ const v = form.getFieldValue('sshTunnels').filter(i => i.id !== id)
68
+ form.setFieldsValue({
69
+ sshTunnels: v
70
+ })
71
+ formChild.resetFields()
72
+ }
73
+ const cols = [
74
+ {
75
+ title: 'NO.',
76
+ dataIndex: 'index',
77
+ key: 'index',
78
+ render: (k) => k
79
+ }, {
80
+ title: e('sshTunnel'),
81
+ key: 'sshTunnel',
82
+ render: (k, item) => {
83
+ // sshTunnel is forwardRemoteToLocal or forwardLocalToRemote
84
+ const {
85
+ sshTunnel,
86
+ sshTunnelRemoteHost = '127.0.0.1',
87
+ sshTunnelRemotePort,
88
+ sshTunnelLocalHost = '127.0.0.1',
89
+ sshTunnelLocalPort,
90
+ name
91
+ } = item
92
+ const to = sshTunnel === 'forwardRemoteToLocal'
93
+ ? `${s('local')}:${sshTunnelLocalHost}:${sshTunnelLocalPort}`
94
+ : `${s('remote')}:${sshTunnelRemoteHost}:${sshTunnelRemotePort}`
95
+ const from = sshTunnel === 'forwardRemoteToLocal'
96
+ ? `${s('remote')}:${sshTunnelRemoteHost}:${sshTunnelRemotePort}`
97
+ : `${s('local')}:${sshTunnelLocalHost}:${sshTunnelLocalPort}`
98
+ return (
99
+ <span>
100
+ {name ? `[${name}] ` : ''}→ {from} → {to}
101
+ </span>
102
+ )
103
+ }
104
+ }, {
105
+ title: m('del'),
106
+ key: 'op',
107
+ dataIndex: 'id',
108
+ render: (id) => {
109
+ return (
110
+ <MinusCircleFilled
111
+ className='pointer'
112
+ onClick={() => remove(id)}
113
+ />
114
+ )
115
+ }
116
+ }
117
+ ]
118
+
119
+ function renderList () {
24
120
  return (
25
- <Space
26
- align='center'
27
- key={field.key}
121
+ <FormItem {...tailFormItemLayout}>
122
+ <Table
123
+ columns={cols}
124
+ className='mg3b'
125
+ pagination={false}
126
+ size='small'
127
+ dataSource={list.map((d, i) => {
128
+ return {
129
+ ...d,
130
+ index: i + 1
131
+ }
132
+ })}
133
+ />
134
+ </FormItem>
135
+ )
136
+ }
137
+
138
+ return (
139
+ <div>
140
+ <FormItem
141
+ name='sshTunnels'
142
+ className='hide'
143
+ >
144
+ <Input />
145
+ </FormItem>
146
+ <Form
147
+ form={formChild}
148
+ onFinish={handleFinish}
149
+ initialValues={initialValues}
28
150
  >
151
+ {renderList()}
29
152
  <FormItem
30
153
  label={e('sshTunnel')}
31
- name={[field.name, 'sshTunnel']}
154
+ name='sshTunnel'
155
+ {...formItemLayout}
32
156
  required
33
157
  >
34
158
  <RadioGroup>
@@ -48,87 +172,85 @@ export default function renderSshTunnel () {
48
172
  </RadioButton>
49
173
  </RadioGroup>
50
174
  </FormItem>
51
- <Space.Compact className='mg2x'>
52
- <FormItem
53
- label={e('destination')}
54
- name={[field.name, 'sshTunnelRemoteHost']}
55
- initialValue='127.0.0.1'
56
- required
57
- >
58
- <Input
59
- className='compact-input'
60
- placeholder={e('host')}
61
- />
62
- </FormItem>
63
- <FormItem
64
- label=''
65
- name={[field.name, 'sshTunnelRemotePort']}
66
- initialValue={22}
67
- required
68
- >
69
- <InputNumber
70
- min={1}
71
- max={65535}
72
- // addonBefore={e('remotePort')}
73
- className='compact-input'
74
- placeholder={e('port')}
75
- />
76
- </FormItem>
77
- </Space.Compact>
78
175
  <FormItem
79
- label={e('localPort')}
80
- name={[field.name, 'sshTunnelLocalPort']}
81
- initialValue={22}
176
+ label={s('remote')}
177
+ {...formItemLayout}
82
178
  required
83
- className='mg2x'
179
+ className='ssh-tunnels-host'
84
180
  >
85
- <InputNumber
86
- min={1}
87
- max={65535}
88
- // addonBefore={e('localPort')}
89
- className='compact-input'
90
- placeholder={e('port')}
181
+ <Space.Compact>
182
+ <FormItem
183
+ name='sshTunnelRemoteHost'
184
+ label=''
185
+ required
186
+ >
187
+ <Input
188
+ placeholder={e('host')}
189
+ />
190
+ </FormItem>
191
+ <FormItem
192
+ label=''
193
+ name='sshTunnelRemotePort'
194
+ required
195
+ >
196
+ <InputNumber
197
+ min={1}
198
+ max={65535}
199
+ placeholder={e('port')}
200
+ />
201
+ </FormItem>
202
+ </Space.Compact>
203
+ </FormItem>
204
+ <FormItem
205
+ label={s('local')}
206
+ {...formItemLayout}
207
+ required
208
+ className='ssh-tunnels-host'
209
+ >
210
+ <Space.Compact>
211
+ <FormItem
212
+ name='sshTunnelLocalHost'
213
+ label=''
214
+ required
215
+ >
216
+ <Input
217
+ placeholder={e('host')}
218
+ />
219
+ </FormItem>
220
+ <FormItem
221
+ label=''
222
+ name='sshTunnelLocalPort'
223
+ required
224
+ >
225
+ <InputNumber
226
+ min={1}
227
+ max={65535}
228
+ // addonBefore={e('localPort')}
229
+ placeholder={e('port')}
230
+ />
231
+ </FormItem>
232
+ </Space.Compact>
233
+ </FormItem>
234
+ <FormItem
235
+ name='name'
236
+ label={s('name')}
237
+ {...formItemLayout}
238
+ >
239
+ <Input
240
+ placeholder={e('name')}
91
241
  />
92
242
  </FormItem>
93
- <Button
94
- icon={<MinusCircleOutlined />}
95
- onClick={() => remove(field.name)}
96
- className='mg24b'
97
- />
98
- </Space>
99
- )
100
- }
101
-
102
- return [
103
- <FormList
104
- {...formItemLayout}
105
- label={e('sshTunnel')}
106
- name='sshTunnels'
107
- key='sshTunnels'
108
- >
109
- {
110
- (fields, { add, remove }, { errors }) => {
111
- return (
112
- <div>
113
- {
114
- fields.map((field, i) => {
115
- return renderItem(field, i, add, remove)
116
- })
117
- }
118
- <FormItem>
119
- <Button
120
- type='dashed'
121
- onClick={() => add()}
122
- block
123
- icon={<PlusOutlined />}
124
- >
125
- {e('sshTunnel')}
126
- </Button>
127
- </FormItem>
128
- </div>
129
- )
130
- }
131
- }
132
- </FormList>
133
- ]
243
+ <FormItem {...tailFormItemLayout} className='mg60b'>
244
+ <Button
245
+ type='default'
246
+ htmlType='button'
247
+ icon={<PlusOutlined />}
248
+ onClick={onSubmit}
249
+ >
250
+ {e('sshTunnel')}
251
+ </Button>
252
+ </FormItem>
253
+ </Form>
254
+ </div>
255
+ )
134
256
  }
@@ -65,7 +65,7 @@ export default function BookmarkFormUI (props) {
65
65
  encode: encodes[0],
66
66
  envLang: defaultEnvLang,
67
67
  enableSsh: true,
68
- sshTunnels: [{}],
68
+ sshTunnels: [],
69
69
  runScripts: [{}],
70
70
  category: initBookmarkGroupId,
71
71
  connectionHoppings: []
@@ -12,6 +12,9 @@ import CssOverwrite from './css-overwrite'
12
12
  import UiTheme from './ui-theme'
13
13
  import CustomCss from './custom-css.jsx'
14
14
  import TerminalInteractive from '../terminal/terminal-interactive'
15
+ import ConfirmModalStore from '../sftp/confirm-modal-store.jsx'
16
+ import TransferConflictStore from '../sftp/transfer-conflict-store.jsx'
17
+ import TransportsActionStore from '../sftp/transports-action-store.jsx'
15
18
  import classnames from 'classnames'
16
19
  import ShortcutControl from '../shortcuts/shortcut-control.jsx'
17
20
  import { isMac, isWin } from '../../common/constants'
@@ -149,6 +152,17 @@ export default class Index extends Component {
149
152
  />
150
153
  </div>
151
154
  <ContextMenu store={store} />
155
+ <ConfirmModalStore
156
+ store={store}
157
+ />
158
+ <TransferConflictStore
159
+ store={store}
160
+ _fileTransfers={store._fileTransfers}
161
+ />
162
+ <TransportsActionStore
163
+ store={store}
164
+ _fileTransfers={store._fileTransfers}
165
+ />
152
166
  </div>
153
167
  </ConfigProvider>
154
168
  )
@@ -182,7 +182,7 @@ export default class QuickCommandsFooterBox extends Component {
182
182
  sortArray (array, keyword, labels, qmSortByFrequency) {
183
183
  const sorters = [
184
184
  (obj) => !(keyword && obj.name.toLowerCase().includes(keyword)),
185
- (obj) => !labels.some((label) => obj.labels.includes(label))
185
+ (obj) => !labels.some((label) => (obj.labels || []).includes(label))
186
186
  ]
187
187
  if (qmSortByFrequency) {
188
188
  sorters.push((obj) => -(obj.clickCount || 0))
@@ -194,9 +194,10 @@ export default class QuickCommandsFooterBox extends Component {
194
194
  const {
195
195
  openQuickCommandBar,
196
196
  pinnedQuickCommandBar,
197
- qmSortByFrequency
197
+ qmSortByFrequency,
198
+ inActiveTerminal
198
199
  } = this.props.store
199
- if (!openQuickCommandBar && !pinnedQuickCommandBar) {
200
+ if ((!openQuickCommandBar && !pinnedQuickCommandBar) || !inActiveTerminal) {
200
201
  return null
201
202
  }
202
203
  const all = this.props.store.currentQuickCommands
@@ -210,7 +211,7 @@ export default class QuickCommandsFooterBox extends Component {
210
211
  return {
211
212
  ...d,
212
213
  nameMatch: keyword && d.name.toLowerCase().includes(keyword),
213
- labelMatch: labels.some((label) => d.labels.includes(label))
214
+ labelMatch: labels.some((label) => (d.labels || []).includes(label))
214
215
  }
215
216
  })
216
217
  const sprops = {