@electerm/electerm-react 2.3.181 → 2.3.190
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/components/bookmark-form/common/fields.jsx +1 -1
- package/client/components/bookmark-form/common/ssh-auth-selector.jsx +36 -32
- package/client/components/bookmark-form/common/ssh-auth-type-selector.jsx +4 -1
- package/client/components/bookmark-form/config/common-fields.js +29 -23
- package/client/components/bookmark-form/config/ftp.js +5 -5
- package/client/components/bookmark-form/config/local.js +9 -9
- package/client/components/bookmark-form/config/rdp.js +3 -3
- package/client/components/bookmark-form/config/serial.js +4 -4
- package/client/components/bookmark-form/config/ssh.js +3 -2
- package/client/components/bookmark-form/config/telnet.js +2 -2
- package/client/components/bookmark-form/config/vnc.js +5 -5
- package/client/components/bookmark-form/config/web.js +3 -3
- package/client/components/quick-commands/quick-commands-form-elem.jsx +9 -39
- package/client/components/setting-panel/hotkey.jsx +132 -0
- package/client/components/setting-panel/setting-common.jsx +8 -62
- package/client/components/shortcuts/get-key-char.js +1 -1
- package/client/components/shortcuts/shortcut-control.jsx +5 -0
- package/client/components/shortcuts/shortcut-editor.jsx +3 -0
- package/client/components/shortcuts/shortcut-handler.js +8 -0
- package/client/components/shortcuts/shortcut-utils.js +49 -0
- package/client/components/shortcuts/shortcuts-defaults.js +5 -0
- package/client/components/shortcuts/shortcuts.jsx +4 -40
- package/client/components/sidebar/index.jsx +3 -0
- package/client/components/tabs/tab.jsx +11 -0
- package/client/store/tab.js +11 -0
- package/package.json +1 -1
|
@@ -101,7 +101,7 @@ export function renderFormItem (item, formItemLayout, form, ctxProps, index) {
|
|
|
101
101
|
const formItemProps = {
|
|
102
102
|
...formItemLayout,
|
|
103
103
|
className: cls,
|
|
104
|
-
label,
|
|
104
|
+
label: typeof label === 'string' ? label : label(),
|
|
105
105
|
name,
|
|
106
106
|
rules,
|
|
107
107
|
valuePropName,
|
|
@@ -26,14 +26,44 @@ export default function renderAuth (props) {
|
|
|
26
26
|
formItemName = 'password',
|
|
27
27
|
profileFilter = (d) => d
|
|
28
28
|
} = props
|
|
29
|
-
const
|
|
29
|
+
const commonBeforeUpload = (fieldName) => async (file) => {
|
|
30
30
|
const filePath = getFilePath(file)
|
|
31
|
-
const
|
|
31
|
+
const content = await window.fs.readFile(filePath)
|
|
32
32
|
form.setFieldsValue({
|
|
33
|
-
|
|
33
|
+
[fieldName]: content
|
|
34
34
|
})
|
|
35
35
|
return false
|
|
36
36
|
}
|
|
37
|
+
const renderKeyField = (key, label, desc) => (
|
|
38
|
+
<FormItem
|
|
39
|
+
{...formItemLayout}
|
|
40
|
+
label={e(label)}
|
|
41
|
+
hasFeedback
|
|
42
|
+
key={key}
|
|
43
|
+
className='mg1b'
|
|
44
|
+
rules={[{
|
|
45
|
+
max: 13000, message: '13000 chars max'
|
|
46
|
+
}]}
|
|
47
|
+
>
|
|
48
|
+
<FormItem noStyle name={key}>
|
|
49
|
+
<TextArea
|
|
50
|
+
placeholder={e(desc)}
|
|
51
|
+
autoSize={{ minRows: 1 }}
|
|
52
|
+
/>
|
|
53
|
+
</FormItem>
|
|
54
|
+
<Upload
|
|
55
|
+
beforeUpload={commonBeforeUpload(key)}
|
|
56
|
+
fileList={[]}
|
|
57
|
+
>
|
|
58
|
+
<Button
|
|
59
|
+
type='dashed'
|
|
60
|
+
className='mg2b mg1t'
|
|
61
|
+
>
|
|
62
|
+
{e('importFromFile')}
|
|
63
|
+
</Button>
|
|
64
|
+
</Upload>
|
|
65
|
+
</FormItem>
|
|
66
|
+
)
|
|
37
67
|
if (authType === 'password') {
|
|
38
68
|
const opts = {
|
|
39
69
|
options: uniqBy(
|
|
@@ -95,34 +125,7 @@ export default function renderAuth (props) {
|
|
|
95
125
|
)
|
|
96
126
|
}
|
|
97
127
|
return [
|
|
98
|
-
|
|
99
|
-
{...formItemLayout}
|
|
100
|
-
label={e('privateKey')}
|
|
101
|
-
hasFeedback
|
|
102
|
-
key='privateKey'
|
|
103
|
-
className='mg1b'
|
|
104
|
-
rules={[{
|
|
105
|
-
max: 13000, message: '13000 chars max'
|
|
106
|
-
}]}
|
|
107
|
-
>
|
|
108
|
-
<FormItem noStyle name='privateKey'>
|
|
109
|
-
<TextArea
|
|
110
|
-
placeholder={e('privateKeyDesc')}
|
|
111
|
-
autoSize={{ minRows: 1 }}
|
|
112
|
-
/>
|
|
113
|
-
</FormItem>
|
|
114
|
-
<Upload
|
|
115
|
-
beforeUpload={beforeUpload}
|
|
116
|
-
fileList={[]}
|
|
117
|
-
>
|
|
118
|
-
<Button
|
|
119
|
-
type='dashed'
|
|
120
|
-
className='mg2b mg1t'
|
|
121
|
-
>
|
|
122
|
-
{e('importFromFile')}
|
|
123
|
-
</Button>
|
|
124
|
-
</Upload>
|
|
125
|
-
</FormItem>,
|
|
128
|
+
renderKeyField('privateKey', 'privateKey', 'privateKeyDesc'),
|
|
126
129
|
<FormItem
|
|
127
130
|
key='passphrase'
|
|
128
131
|
{...formItemLayout}
|
|
@@ -136,6 +139,7 @@ export default function renderAuth (props) {
|
|
|
136
139
|
<Password
|
|
137
140
|
placeholder={e('passphraseDesc')}
|
|
138
141
|
/>
|
|
139
|
-
</FormItem
|
|
142
|
+
</FormItem>,
|
|
143
|
+
renderKeyField('certificate', 'certificate', 'certificate')
|
|
140
144
|
]
|
|
141
145
|
}
|
|
@@ -25,9 +25,12 @@ export default function SshAuthTypeSelector ({ handleChangeAuthType, filterAuthT
|
|
|
25
25
|
>
|
|
26
26
|
{
|
|
27
27
|
authTypesFiltered.map(t => {
|
|
28
|
+
const str = t === 'privateKey'
|
|
29
|
+
? e(t) + '/' + e('certificate')
|
|
30
|
+
: e(t)
|
|
28
31
|
return (
|
|
29
32
|
<RadioButton value={t} key={t}>
|
|
30
|
-
{
|
|
33
|
+
{str}
|
|
31
34
|
</RadioButton>
|
|
32
35
|
)
|
|
33
36
|
})
|
|
@@ -15,65 +15,65 @@ export const commonFields = {
|
|
|
15
15
|
host: {
|
|
16
16
|
type: 'colorTitle',
|
|
17
17
|
name: 'host',
|
|
18
|
-
label: e('host'),
|
|
18
|
+
label: () => e('host'),
|
|
19
19
|
rules: [{ required: true, message: e('host') + ' required' }]
|
|
20
20
|
},
|
|
21
21
|
|
|
22
22
|
colorTitle: {
|
|
23
23
|
type: 'colorTitle',
|
|
24
24
|
name: 'title',
|
|
25
|
-
label: e('title')
|
|
25
|
+
label: () => e('title')
|
|
26
26
|
},
|
|
27
27
|
|
|
28
28
|
title: {
|
|
29
29
|
type: 'input',
|
|
30
30
|
name: 'title',
|
|
31
|
-
label: e('title')
|
|
31
|
+
label: () => e('title')
|
|
32
32
|
},
|
|
33
33
|
|
|
34
34
|
username: {
|
|
35
35
|
type: 'input',
|
|
36
36
|
name: 'username',
|
|
37
|
-
label: e('username')
|
|
37
|
+
label: () => e('username')
|
|
38
38
|
},
|
|
39
39
|
|
|
40
40
|
password: {
|
|
41
41
|
type: 'password',
|
|
42
42
|
name: 'password',
|
|
43
|
-
label: e('password')
|
|
43
|
+
label: () => e('password')
|
|
44
44
|
},
|
|
45
45
|
|
|
46
46
|
loginPrompt: {
|
|
47
47
|
type: 'input',
|
|
48
48
|
name: 'loginPrompt',
|
|
49
|
-
label: e('loginPrompt'),
|
|
49
|
+
label: () => e('loginPrompt'),
|
|
50
50
|
props: { placeholder: '/login[: ]*$/i' }
|
|
51
51
|
},
|
|
52
52
|
|
|
53
53
|
passwordPrompt: {
|
|
54
54
|
type: 'input',
|
|
55
55
|
name: 'passwordPrompt',
|
|
56
|
-
label: e('passwordPrompt'),
|
|
56
|
+
label: () => e('passwordPrompt'),
|
|
57
57
|
props: { placeholder: '/password[: ]*$/i' }
|
|
58
58
|
},
|
|
59
59
|
|
|
60
60
|
port: {
|
|
61
61
|
type: 'number',
|
|
62
62
|
name: 'port',
|
|
63
|
-
label: e('port'),
|
|
63
|
+
label: () => e('port'),
|
|
64
64
|
rules: [{ required: true, message: 'port required' }]
|
|
65
65
|
},
|
|
66
66
|
|
|
67
67
|
description: {
|
|
68
68
|
type: 'textarea',
|
|
69
69
|
name: 'description',
|
|
70
|
-
label: e('description')
|
|
70
|
+
label: () => e('description')
|
|
71
71
|
},
|
|
72
72
|
|
|
73
73
|
category: {
|
|
74
74
|
type: 'categorySelect',
|
|
75
75
|
name: 'category',
|
|
76
|
-
label: e('category')
|
|
76
|
+
label: () => e('category')
|
|
77
77
|
},
|
|
78
78
|
|
|
79
79
|
type: {
|
|
@@ -106,13 +106,13 @@ export const commonFields = {
|
|
|
106
106
|
interactiveValues: {
|
|
107
107
|
type: 'textarea',
|
|
108
108
|
name: 'interactiveValues',
|
|
109
|
-
label: e('interactiveValues')
|
|
109
|
+
label: () => e('interactiveValues')
|
|
110
110
|
},
|
|
111
111
|
|
|
112
112
|
encode: {
|
|
113
113
|
type: 'select',
|
|
114
114
|
name: 'encode',
|
|
115
|
-
label: e('encode'),
|
|
115
|
+
label: () => e('encode'),
|
|
116
116
|
options: encodes.map(k => ({ label: k.toUpperCase(), value: k }))
|
|
117
117
|
},
|
|
118
118
|
|
|
@@ -128,7 +128,7 @@ export const commonFields = {
|
|
|
128
128
|
terminalType: {
|
|
129
129
|
type: 'autocomplete',
|
|
130
130
|
name: 'term',
|
|
131
|
-
label: e('terminalType'),
|
|
131
|
+
label: () => e('terminalType'),
|
|
132
132
|
rules: [{ required: true, message: 'terminal type required' }],
|
|
133
133
|
options: terminalTypes.map(t => ({ label: t, value: t }))
|
|
134
134
|
},
|
|
@@ -136,14 +136,14 @@ export const commonFields = {
|
|
|
136
136
|
displayRaw: {
|
|
137
137
|
type: 'switch',
|
|
138
138
|
name: 'displayRaw',
|
|
139
|
-
label: e('displayRaw'),
|
|
139
|
+
label: () => e('displayRaw'),
|
|
140
140
|
valuePropName: 'checked'
|
|
141
141
|
},
|
|
142
142
|
|
|
143
143
|
fontFamily: {
|
|
144
144
|
type: 'input',
|
|
145
145
|
name: 'fontFamily',
|
|
146
|
-
label: e('fontFamily'),
|
|
146
|
+
label: () => e('fontFamily'),
|
|
147
147
|
rules: [{ max: 130, message: '130 chars max' }],
|
|
148
148
|
props: { placeholder: defaultSettings.fontFamily }
|
|
149
149
|
},
|
|
@@ -151,7 +151,7 @@ export const commonFields = {
|
|
|
151
151
|
fontSize: {
|
|
152
152
|
type: 'number',
|
|
153
153
|
name: 'fontSize',
|
|
154
|
-
label: e('fontSize'),
|
|
154
|
+
label: () => e('fontSize'),
|
|
155
155
|
props: {
|
|
156
156
|
min: 9,
|
|
157
157
|
max: 65535,
|
|
@@ -163,7 +163,7 @@ export const commonFields = {
|
|
|
163
163
|
keepaliveInterval: {
|
|
164
164
|
type: 'number',
|
|
165
165
|
name: 'keepaliveInterval',
|
|
166
|
-
label: e('keepaliveIntervalDesc'),
|
|
166
|
+
label: () => e('keepaliveIntervalDesc'),
|
|
167
167
|
props: {
|
|
168
168
|
min: 0,
|
|
169
169
|
max: 20000000,
|
|
@@ -174,13 +174,13 @@ export const commonFields = {
|
|
|
174
174
|
terminalBackground: {
|
|
175
175
|
type: 'terminalBackground',
|
|
176
176
|
name: 'terminalBackground',
|
|
177
|
-
label: e('terminalBackgroundImage')
|
|
177
|
+
label: () => e('terminalBackgroundImage')
|
|
178
178
|
},
|
|
179
179
|
|
|
180
180
|
proxy: {
|
|
181
181
|
type: 'proxy',
|
|
182
182
|
name: '__proxy__',
|
|
183
|
-
label: e('proxy')
|
|
183
|
+
label: () => e('proxy')
|
|
184
184
|
},
|
|
185
185
|
|
|
186
186
|
x11: {
|
|
@@ -243,7 +243,7 @@ export const sshSettings = [
|
|
|
243
243
|
{
|
|
244
244
|
type: 'switch',
|
|
245
245
|
name: 'ignoreKeyboardInteractive',
|
|
246
|
-
label: e('ignoreKeyboardInteractive'),
|
|
246
|
+
label: () => e('ignoreKeyboardInteractive'),
|
|
247
247
|
valuePropName: 'checked'
|
|
248
248
|
},
|
|
249
249
|
...terminalSettings.slice(0, -1), // All except terminalBackground
|
|
@@ -271,6 +271,12 @@ export const sshAuthFields = [
|
|
|
271
271
|
{ type: 'sshAuthTypeSelector', name: 'authType', label: '' },
|
|
272
272
|
{ type: 'sshAuthSelector', name: '__auth__', label: '', formItemName: 'password' },
|
|
273
273
|
commonFields.port,
|
|
274
|
+
{
|
|
275
|
+
type: 'switch',
|
|
276
|
+
name: 'useSshAgent',
|
|
277
|
+
label: () => e('useSshAgent'),
|
|
278
|
+
valuePropName: 'checked'
|
|
279
|
+
},
|
|
274
280
|
commonFields.runScripts,
|
|
275
281
|
commonFields.description,
|
|
276
282
|
commonFields.setEnv,
|
|
@@ -305,18 +311,18 @@ export const telnetAuthFields = [
|
|
|
305
311
|
// Common tab configurations - functions to ensure translation happens at render time
|
|
306
312
|
export const quickCommandsTab = () => ({
|
|
307
313
|
key: 'quickCommands',
|
|
308
|
-
label: e('quickCommands'),
|
|
314
|
+
label: () => e('quickCommands'),
|
|
309
315
|
fields: [commonFields.quickCommands]
|
|
310
316
|
})
|
|
311
317
|
|
|
312
318
|
export const sshTunnelTab = () => ({
|
|
313
319
|
key: 'tunnel',
|
|
314
|
-
label: e('sshTunnel'),
|
|
320
|
+
label: () => e('sshTunnel'),
|
|
315
321
|
fields: [commonFields.sshTunnels]
|
|
316
322
|
})
|
|
317
323
|
|
|
318
324
|
export const connectionHoppingTab = () => ({
|
|
319
325
|
key: 'connectionHopping',
|
|
320
|
-
label: e('connectionHopping'),
|
|
326
|
+
label: () => e('connectionHopping'),
|
|
321
327
|
fields: [commonFields.connectionHopping]
|
|
322
328
|
})
|
|
@@ -22,16 +22,16 @@ const ftpConfig = {
|
|
|
22
22
|
tabs: () => [
|
|
23
23
|
{
|
|
24
24
|
key: 'auth',
|
|
25
|
-
label: e('auth'),
|
|
25
|
+
label: () => e('auth'),
|
|
26
26
|
fields: [
|
|
27
27
|
commonFields.category,
|
|
28
28
|
commonFields.colorTitle,
|
|
29
|
-
{ type: 'input', name: 'host', label: e('host'), rules: [{ required: true, message: e('host') + ' required' }] },
|
|
29
|
+
{ type: 'input', name: 'host', label: () => e('host'), rules: [{ required: true, message: e('host') + ' required' }] },
|
|
30
30
|
commonFields.port,
|
|
31
31
|
{ type: 'profileItem', name: '__profile__', label: '', profileFilter: d => !isEmpty(d.ftp) },
|
|
32
|
-
{ type: 'input', name: 'user', label: e('username') },
|
|
33
|
-
{ type: 'password', name: 'password', label: e('password') },
|
|
34
|
-
{ type: 'switch', name: 'secure', label: e('secure'), valuePropName: 'checked' },
|
|
32
|
+
{ type: 'input', name: 'user', label: () => e('username') },
|
|
33
|
+
{ type: 'password', name: 'password', label: () => e('password') },
|
|
34
|
+
{ type: 'switch', name: 'secure', label: () => e('secure'), valuePropName: 'checked' },
|
|
35
35
|
commonFields.type
|
|
36
36
|
]
|
|
37
37
|
}
|
|
@@ -26,7 +26,7 @@ const localConfig = {
|
|
|
26
26
|
tabs: () => [
|
|
27
27
|
{
|
|
28
28
|
key: 'auth',
|
|
29
|
-
label: e('auth'),
|
|
29
|
+
label: () => e('auth'),
|
|
30
30
|
fields: [
|
|
31
31
|
commonFields.category,
|
|
32
32
|
commonFields.colorTitle,
|
|
@@ -37,7 +37,7 @@ const localConfig = {
|
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
key: 'settings',
|
|
40
|
-
label: e('settings'),
|
|
40
|
+
label: () => e('settings'),
|
|
41
41
|
fields: [
|
|
42
42
|
{
|
|
43
43
|
type: 'input',
|
|
@@ -48,27 +48,27 @@ const localConfig = {
|
|
|
48
48
|
{
|
|
49
49
|
type: 'autocomplete',
|
|
50
50
|
name: 'term',
|
|
51
|
-
label: e('terminalType'),
|
|
51
|
+
label: () => e('terminalType'),
|
|
52
52
|
rules: [{ required: true, message: 'terminal type required' }],
|
|
53
53
|
options: terminalTypes.map(t => ({ label: t, value: t }))
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
type: 'switch',
|
|
57
57
|
name: 'displayRaw',
|
|
58
|
-
label: e('displayRaw'),
|
|
58
|
+
label: () => e('displayRaw'),
|
|
59
59
|
valuePropName: 'checked'
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
62
|
type: 'input',
|
|
63
63
|
name: 'fontFamily',
|
|
64
|
-
label: e('fontFamily'),
|
|
64
|
+
label: () => e('fontFamily'),
|
|
65
65
|
rules: [{ max: 130, message: '130 chars max' }],
|
|
66
66
|
props: { placeholder: defaultSettings.fontFamily }
|
|
67
67
|
},
|
|
68
68
|
{
|
|
69
69
|
type: 'number',
|
|
70
70
|
name: 'fontSize',
|
|
71
|
-
label: e('fontSize'),
|
|
71
|
+
label: () => e('fontSize'),
|
|
72
72
|
props: {
|
|
73
73
|
min: 9,
|
|
74
74
|
max: 65535,
|
|
@@ -79,19 +79,19 @@ const localConfig = {
|
|
|
79
79
|
{
|
|
80
80
|
type: 'number',
|
|
81
81
|
name: 'keepaliveInterval',
|
|
82
|
-
label: e('keepaliveIntervalDesc'),
|
|
82
|
+
label: () => e('keepaliveIntervalDesc'),
|
|
83
83
|
props: {
|
|
84
84
|
min: 0,
|
|
85
85
|
max: 20000000,
|
|
86
86
|
step: 1000
|
|
87
87
|
}
|
|
88
88
|
},
|
|
89
|
-
{ type: 'terminalBackground', name: 'terminalBackground', label: e('terminalBackgroundImage') }
|
|
89
|
+
{ type: 'terminalBackground', name: 'terminalBackground', label: () => e('terminalBackgroundImage') }
|
|
90
90
|
]
|
|
91
91
|
},
|
|
92
92
|
{
|
|
93
93
|
key: 'quickCommands',
|
|
94
|
-
label: e('quickCommands'),
|
|
94
|
+
label: () => e('quickCommands'),
|
|
95
95
|
fields: [
|
|
96
96
|
{ type: 'quickCommands', name: '__quick__', label: '' }
|
|
97
97
|
]
|
|
@@ -19,18 +19,18 @@ const rdpConfig = {
|
|
|
19
19
|
tabs: () => [
|
|
20
20
|
{
|
|
21
21
|
key: 'auth',
|
|
22
|
-
label: e('auth'),
|
|
22
|
+
label: () => e('auth'),
|
|
23
23
|
fields: [
|
|
24
24
|
{ type: 'rdpWarning', name: 'rdpWarning' },
|
|
25
25
|
commonFields.category,
|
|
26
26
|
commonFields.colorTitle,
|
|
27
|
-
{ type: 'input', name: 'host', label: e('host'), rules: [{ required: true, message: e('host') + ' required' }] },
|
|
27
|
+
{ type: 'input', name: 'host', label: () => e('host'), rules: [{ required: true, message: e('host') + ' required' }] },
|
|
28
28
|
commonFields.port,
|
|
29
29
|
{ type: 'profileItem', name: '__profile__', label: '', profileFilter: d => !isEmpty(d.rdp) },
|
|
30
30
|
{ ...commonFields.username, rules: [{ required: true, message: e('username') + ' required' }] },
|
|
31
31
|
{ ...commonFields.password, rules: [{ required: true, message: e('password') + ' required' }] },
|
|
32
32
|
commonFields.description,
|
|
33
|
-
{ type: 'input', name: 'domain', label: e('domain') },
|
|
33
|
+
{ type: 'input', name: 'domain', label: () => e('domain') },
|
|
34
34
|
commonFields.type
|
|
35
35
|
]
|
|
36
36
|
}
|
|
@@ -31,7 +31,7 @@ const serialConfig = {
|
|
|
31
31
|
tabs: () => [
|
|
32
32
|
{
|
|
33
33
|
key: 'auth',
|
|
34
|
-
label: e('auth'),
|
|
34
|
+
label: () => e('auth'),
|
|
35
35
|
fields: [
|
|
36
36
|
commonFields.category,
|
|
37
37
|
commonFields.colorTitle,
|
|
@@ -64,14 +64,14 @@ const serialConfig = {
|
|
|
64
64
|
},
|
|
65
65
|
{
|
|
66
66
|
key: 'settings',
|
|
67
|
-
label: e('settings'),
|
|
67
|
+
label: () => e('settings'),
|
|
68
68
|
fields: [
|
|
69
|
-
{ type: 'terminalBackground', name: 'terminalBackground', label: e('terminalBackgroundImage') }
|
|
69
|
+
{ type: 'terminalBackground', name: 'terminalBackground', label: () => e('terminalBackgroundImage') }
|
|
70
70
|
]
|
|
71
71
|
},
|
|
72
72
|
{
|
|
73
73
|
key: 'quickCommands',
|
|
74
|
-
label: e('quickCommands'),
|
|
74
|
+
label: () => e('quickCommands'),
|
|
75
75
|
fields: [
|
|
76
76
|
{ type: 'quickCommands', name: '__quick__', label: '' }
|
|
77
77
|
]
|
|
@@ -20,6 +20,7 @@ const sshConfig = {
|
|
|
20
20
|
enableSftp: true,
|
|
21
21
|
sshTunnels: [],
|
|
22
22
|
connectionHoppings: [],
|
|
23
|
+
useSshAgent: true,
|
|
23
24
|
serverHostKey: [],
|
|
24
25
|
cipher: [],
|
|
25
26
|
...getTerminalDefaults(store),
|
|
@@ -32,12 +33,12 @@ const sshConfig = {
|
|
|
32
33
|
tabs: () => [
|
|
33
34
|
{
|
|
34
35
|
key: 'auth',
|
|
35
|
-
label: e('auth'),
|
|
36
|
+
label: () => e('auth'),
|
|
36
37
|
fields: sshAuthFields
|
|
37
38
|
},
|
|
38
39
|
{
|
|
39
40
|
key: 'settings',
|
|
40
|
-
label: e('settings'),
|
|
41
|
+
label: () => e('settings'),
|
|
41
42
|
fields: sshSettings
|
|
42
43
|
},
|
|
43
44
|
quickCommandsTab(),
|
|
@@ -31,12 +31,12 @@ const telnetConfig = {
|
|
|
31
31
|
tabs: () => [
|
|
32
32
|
{
|
|
33
33
|
key: 'auth',
|
|
34
|
-
label: e('auth'),
|
|
34
|
+
label: () => e('auth'),
|
|
35
35
|
fields: telnetAuthFields
|
|
36
36
|
},
|
|
37
37
|
{
|
|
38
38
|
key: 'settings',
|
|
39
|
-
label: e('settings'),
|
|
39
|
+
label: () => e('settings'),
|
|
40
40
|
fields: terminalSettings
|
|
41
41
|
},
|
|
42
42
|
quickCommandsTab()
|
|
@@ -23,16 +23,16 @@ const vncConfig = {
|
|
|
23
23
|
tabs: () => [
|
|
24
24
|
{
|
|
25
25
|
key: 'auth',
|
|
26
|
-
label: e('auth'),
|
|
26
|
+
label: () => e('auth'),
|
|
27
27
|
fields: [
|
|
28
28
|
{ type: 'vncWarning', name: 'vncWarning' },
|
|
29
29
|
commonFields.category,
|
|
30
30
|
commonFields.colorTitle,
|
|
31
|
-
{ type: 'input', name: 'host', label: e('host'), rules: [{ required: true, message: e('host') + ' required' }] },
|
|
31
|
+
{ type: 'input', name: 'host', label: () => e('host'), rules: [{ required: true, message: e('host') + ' required' }] },
|
|
32
32
|
commonFields.port,
|
|
33
|
-
{ type: 'switch', name: 'viewOnly', label: e('viewOnly'), valuePropName: 'checked' },
|
|
34
|
-
{ type: 'switch', name: 'clipViewport', label: e('clipViewport'), valuePropName: 'checked' },
|
|
35
|
-
{ type: 'switch', name: 'scaleViewport', label: e('scaleViewport'), valuePropName: 'checked' },
|
|
33
|
+
{ type: 'switch', name: 'viewOnly', label: () => e('viewOnly'), valuePropName: 'checked' },
|
|
34
|
+
{ type: 'switch', name: 'clipViewport', label: () => e('clipViewport'), valuePropName: 'checked' },
|
|
35
|
+
{ type: 'switch', name: 'scaleViewport', label: () => e('scaleViewport'), valuePropName: 'checked' },
|
|
36
36
|
{ type: 'profileItem', name: '__profile__', label: '', profileFilter: d => !isEmpty(d.vnc) },
|
|
37
37
|
commonFields.username,
|
|
38
38
|
commonFields.password,
|
|
@@ -15,14 +15,14 @@ const webConfig = {
|
|
|
15
15
|
tabs: () => [
|
|
16
16
|
{
|
|
17
17
|
key: 'main',
|
|
18
|
-
label: e('auth'),
|
|
18
|
+
label: () => e('auth'),
|
|
19
19
|
fields: [
|
|
20
20
|
commonFields.category,
|
|
21
21
|
commonFields.colorTitle,
|
|
22
22
|
{
|
|
23
23
|
type: 'input',
|
|
24
24
|
name: 'url',
|
|
25
|
-
label: e('URL'),
|
|
25
|
+
label: () => e('URL'),
|
|
26
26
|
rules: [
|
|
27
27
|
{ required: true, message: e('Please input URL') },
|
|
28
28
|
{
|
|
@@ -34,7 +34,7 @@ const webConfig = {
|
|
|
34
34
|
]
|
|
35
35
|
},
|
|
36
36
|
commonFields.description,
|
|
37
|
-
{ type: 'input', name: 'useragent', label: e('useragent') },
|
|
37
|
+
{ type: 'input', name: 'useragent', label: () => e('useragent') },
|
|
38
38
|
{ type: 'switch', name: 'hideAddressBar', label: 'hideAddressBar', valuePropName: 'checked' },
|
|
39
39
|
commonFields.type
|
|
40
40
|
]
|
|
@@ -11,18 +11,14 @@ import generate from '../../common/uid'
|
|
|
11
11
|
import InputAutoFocus from '../common/input-auto-focus'
|
|
12
12
|
import renderQm from './quick-commands-list-form'
|
|
13
13
|
import ShortcutEdit from '../shortcuts/shortcut-editor'
|
|
14
|
-
import
|
|
14
|
+
import { getKeysTakenData } from '../shortcuts/shortcut-utils'
|
|
15
15
|
import deepCopy from 'json-deep-copy'
|
|
16
|
-
import {
|
|
17
|
-
isMacJs as isMac
|
|
18
|
-
} from '../../common/constants.js'
|
|
19
16
|
import templates from './templates'
|
|
20
17
|
import HelpIcon from '../common/help-icon'
|
|
21
18
|
|
|
22
19
|
const FormItem = Form.Item
|
|
23
20
|
const { Option } = Select
|
|
24
21
|
const e = window.translate
|
|
25
|
-
const shortcutsDefaults = shortcutsDefaultsGen()
|
|
26
22
|
|
|
27
23
|
export default function QuickCommandForm (props) {
|
|
28
24
|
const [form] = Form.useForm()
|
|
@@ -42,41 +38,15 @@ export default function QuickCommandForm (props) {
|
|
|
42
38
|
})
|
|
43
39
|
setShortcut('')
|
|
44
40
|
}
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
const { quickCommands = [] } = store
|
|
48
|
-
|
|
49
|
-
// Gather system shortcuts
|
|
50
|
-
const systemShortcuts = shortcutsDefaults.reduce((p, k) => {
|
|
51
|
-
const propName = isMac ? 'shortcutMac' : 'shortcut'
|
|
52
|
-
const name = k.name + '_' + propName
|
|
53
|
-
const vv = k.readonly ? k[propName] : (shortcuts[name] || k[propName])
|
|
54
|
-
const v = vv
|
|
55
|
-
.split(',')
|
|
56
|
-
.map(f => f.trim())
|
|
57
|
-
.reduce((p, k) => ({
|
|
58
|
-
...p,
|
|
59
|
-
[k]: true
|
|
60
|
-
}), {})
|
|
61
|
-
return {
|
|
62
|
-
...p,
|
|
63
|
-
...v
|
|
64
|
-
}
|
|
65
|
-
}, {})
|
|
41
|
+
const getKeysTaken = () => {
|
|
42
|
+
const keysTaken = getKeysTakenData()
|
|
66
43
|
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
acc[command.shortcut] = true
|
|
71
|
-
}
|
|
72
|
-
return acc
|
|
73
|
-
}, {})
|
|
74
|
-
|
|
75
|
-
// Combine system shortcuts and quick command shortcuts
|
|
76
|
-
return {
|
|
77
|
-
...systemShortcuts,
|
|
78
|
-
...quickCommandShortcuts
|
|
44
|
+
// Exclude current shortcut if editing existing command
|
|
45
|
+
if (formData.shortcut) {
|
|
46
|
+
delete keysTaken[formData.shortcut]
|
|
79
47
|
}
|
|
48
|
+
|
|
49
|
+
return keysTaken
|
|
80
50
|
}
|
|
81
51
|
|
|
82
52
|
async function handleSubmit (res) {
|
|
@@ -126,7 +96,7 @@ export default function QuickCommandForm (props) {
|
|
|
126
96
|
name: uid,
|
|
127
97
|
shortcut
|
|
128
98
|
},
|
|
129
|
-
keysTaken:
|
|
99
|
+
keysTaken: getKeysTaken(),
|
|
130
100
|
store,
|
|
131
101
|
updateConfig,
|
|
132
102
|
handleClear,
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import React, { Component } from 'react'
|
|
2
|
+
import ShortcutEdit from '../shortcuts/shortcut-editor'
|
|
3
|
+
import { getKeysTakenData } from '../shortcuts/shortcut-utils'
|
|
4
|
+
import './setting.styl'
|
|
5
|
+
|
|
6
|
+
const e = window.translate
|
|
7
|
+
|
|
8
|
+
// Bidirectional maps for key conversions
|
|
9
|
+
const MODIFIER_MAP = {
|
|
10
|
+
// Display format -> Electron format
|
|
11
|
+
ctrl: 'CommandOrControl',
|
|
12
|
+
meta: 'CommandOrControl',
|
|
13
|
+
alt: 'Alt',
|
|
14
|
+
shift: 'Shift',
|
|
15
|
+
// Electron format -> Display format
|
|
16
|
+
CommandOrControl: 'ctrl',
|
|
17
|
+
Command: 'meta',
|
|
18
|
+
Control: 'ctrl',
|
|
19
|
+
Alt: 'alt',
|
|
20
|
+
Shift: 'shift',
|
|
21
|
+
Super: 'meta',
|
|
22
|
+
Meta: 'meta'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const KEY_MAP = {
|
|
26
|
+
// Display format -> Electron format
|
|
27
|
+
'←': 'Left',
|
|
28
|
+
'↑': 'Up',
|
|
29
|
+
'→': 'Right',
|
|
30
|
+
'↓': 'Down',
|
|
31
|
+
'▲': 'mouseWheelUp',
|
|
32
|
+
'▼': 'mouseWheelDown',
|
|
33
|
+
enter: 'Return',
|
|
34
|
+
// Electron format -> Display format
|
|
35
|
+
Left: '←',
|
|
36
|
+
Up: '↑',
|
|
37
|
+
Right: '→',
|
|
38
|
+
Down: '↓',
|
|
39
|
+
Return: 'enter',
|
|
40
|
+
Enter: 'enter',
|
|
41
|
+
mouseWheelUp: '▲',
|
|
42
|
+
mouseWheelDown: '▼'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default class HotkeySetting extends Component {
|
|
46
|
+
onChangeHotkey = (name, shortcut) => {
|
|
47
|
+
// Convert shortcut from ShortcutEdit format to Electron accelerator format
|
|
48
|
+
const electronShortcut = this.convertToElectronAccelerator(shortcut)
|
|
49
|
+
return this.props.onSaveConfig({
|
|
50
|
+
[name]: electronShortcut
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
convertToElectronAccelerator = (shortcut) => {
|
|
55
|
+
if (!shortcut) return shortcut
|
|
56
|
+
|
|
57
|
+
// Split the shortcut into parts
|
|
58
|
+
const parts = shortcut.split('+')
|
|
59
|
+
const modifiers = []
|
|
60
|
+
let key = ''
|
|
61
|
+
|
|
62
|
+
// Process each part
|
|
63
|
+
for (const part of parts) {
|
|
64
|
+
const lowerPart = part.toLowerCase()
|
|
65
|
+
if (MODIFIER_MAP[lowerPart]) {
|
|
66
|
+
modifiers.push(MODIFIER_MAP[lowerPart])
|
|
67
|
+
} else {
|
|
68
|
+
// Handle special key mappings
|
|
69
|
+
key = KEY_MAP[part] || part.toUpperCase()
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Combine modifiers and key
|
|
74
|
+
return [...modifiers, key].join('+')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
convertFromElectronAccelerator = (electronShortcut) => {
|
|
78
|
+
if (!electronShortcut) return electronShortcut
|
|
79
|
+
|
|
80
|
+
// Split the shortcut into parts
|
|
81
|
+
const parts = electronShortcut.split('+')
|
|
82
|
+
const modifiers = []
|
|
83
|
+
let key = ''
|
|
84
|
+
|
|
85
|
+
// Process each part
|
|
86
|
+
for (const part of parts) {
|
|
87
|
+
if (MODIFIER_MAP[part]) {
|
|
88
|
+
modifiers.push(MODIFIER_MAP[part])
|
|
89
|
+
} else {
|
|
90
|
+
// Handle special key mappings
|
|
91
|
+
key = KEY_MAP[part] || part.toLowerCase()
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Combine modifiers and key
|
|
96
|
+
return [...modifiers, key].join('+')
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
getKeysTaken = (currentHotkey) => {
|
|
100
|
+
const keysTaken = getKeysTakenData()
|
|
101
|
+
|
|
102
|
+
// Convert current hotkey to display format for comparison
|
|
103
|
+
const currentShortcutDisplay = this.convertFromElectronAccelerator(currentHotkey)
|
|
104
|
+
|
|
105
|
+
// Remove current hotkey from taken keys (allow re-setting the same hotkey)
|
|
106
|
+
delete keysTaken[currentShortcutDisplay]
|
|
107
|
+
|
|
108
|
+
return keysTaken
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
render () {
|
|
112
|
+
const { hotkey } = this.props
|
|
113
|
+
const shortcutProps = {
|
|
114
|
+
data: {
|
|
115
|
+
name: 'hotkey',
|
|
116
|
+
shortcut: this.convertFromElectronAccelerator(hotkey),
|
|
117
|
+
index: 0
|
|
118
|
+
},
|
|
119
|
+
updateConfig: this.onChangeHotkey,
|
|
120
|
+
keysTaken: this.getKeysTaken(hotkey)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<div className='pd2b'>
|
|
125
|
+
<div className='pd1b'>{e('hotkeyDesc')}</div>
|
|
126
|
+
<ShortcutEdit
|
|
127
|
+
{...shortcutProps}
|
|
128
|
+
/>
|
|
129
|
+
</div>
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -32,24 +32,12 @@ import HelpIcon from '../common/help-icon'
|
|
|
32
32
|
import delay from '../../common/wait.js'
|
|
33
33
|
import isColorDark from '../../common/is-color-dark'
|
|
34
34
|
import DeepLinkControl from './deep-link-control'
|
|
35
|
+
import HotkeySetting from './hotkey'
|
|
35
36
|
import './setting.styl'
|
|
36
37
|
|
|
37
38
|
const { Option } = Select
|
|
38
39
|
const e = window.translate
|
|
39
40
|
|
|
40
|
-
const modifiers = [
|
|
41
|
-
'Command',
|
|
42
|
-
'Control',
|
|
43
|
-
'Alt',
|
|
44
|
-
'Shift'
|
|
45
|
-
]
|
|
46
|
-
const keys = [
|
|
47
|
-
...'0123456789~ABCDEFGHIJKLMNOPQRTSUVWXYZ'.split(''),
|
|
48
|
-
...new Array(12).fill(0).map((m, i) => {
|
|
49
|
-
return 'F' + (i + 1)
|
|
50
|
-
})
|
|
51
|
-
]
|
|
52
|
-
|
|
53
41
|
export default class SettingCommon extends Component {
|
|
54
42
|
state = {
|
|
55
43
|
ready: false,
|
|
@@ -142,28 +130,12 @@ export default class SettingCommon extends Component {
|
|
|
142
130
|
)
|
|
143
131
|
}
|
|
144
132
|
|
|
145
|
-
handleChangeModifier = modifier => {
|
|
146
|
-
const { hotkey } = this.props.config
|
|
147
|
-
const key = hotkey.split('+')[1]
|
|
148
|
-
return this.saveConfig({
|
|
149
|
-
hotkey: `${modifier}+${key}`
|
|
150
|
-
})
|
|
151
|
-
}
|
|
152
|
-
|
|
153
133
|
onChangeTimeout = sshReadyTimeout => {
|
|
154
134
|
return this.saveConfig({
|
|
155
135
|
sshReadyTimeout
|
|
156
136
|
})
|
|
157
137
|
}
|
|
158
138
|
|
|
159
|
-
handleChangeKey = key => {
|
|
160
|
-
const { hotkey } = this.props.config
|
|
161
|
-
const modifier = hotkey.split('+')[0]
|
|
162
|
-
return this.saveConfig({
|
|
163
|
-
hotkey: `${modifier}+${key}`
|
|
164
|
-
})
|
|
165
|
-
}
|
|
166
|
-
|
|
167
139
|
handleChangeLang = language => {
|
|
168
140
|
this.setState({
|
|
169
141
|
languageChanged: true
|
|
@@ -211,12 +183,6 @@ export default class SettingCommon extends Component {
|
|
|
211
183
|
this.props.store.setConfig(ext)
|
|
212
184
|
}
|
|
213
185
|
|
|
214
|
-
renderOption = (m, i) => {
|
|
215
|
-
return (
|
|
216
|
-
<Option value={m} key={m + 'opt' + i}>{m}</Option>
|
|
217
|
-
)
|
|
218
|
-
}
|
|
219
|
-
|
|
220
186
|
renderToggle = (name, extra = null) => {
|
|
221
187
|
const checked = !!this.props.config[name]
|
|
222
188
|
return (
|
|
@@ -513,7 +479,6 @@ export default class SettingCommon extends Component {
|
|
|
513
479
|
langs = []
|
|
514
480
|
} = window.et
|
|
515
481
|
const terminalThemes = props.store.getSidebarList(settingMap.terminalThemes)
|
|
516
|
-
const [modifier, key] = hotkey.split('+')
|
|
517
482
|
const pops = {
|
|
518
483
|
onStartSessions: props.config.onStartSessions,
|
|
519
484
|
bookmarks: props.bookmarks,
|
|
@@ -521,35 +486,16 @@ export default class SettingCommon extends Component {
|
|
|
521
486
|
workspaces: props.store.workspaces,
|
|
522
487
|
onChangeStartSessions: this.onChangeStartSessions
|
|
523
488
|
}
|
|
489
|
+
const hotkeyProps = {
|
|
490
|
+
hotkey,
|
|
491
|
+
onSaveConfig: this.saveConfig
|
|
492
|
+
}
|
|
524
493
|
return (
|
|
525
494
|
<div className='form-wrap pd1y pd2x'>
|
|
526
495
|
<h2>{e('settings')}</h2>
|
|
527
|
-
<
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
value={modifier}
|
|
531
|
-
onChange={this.handleChangeModifier}
|
|
532
|
-
className='width100'
|
|
533
|
-
popupMatchSelectWidth={false}
|
|
534
|
-
showSearch
|
|
535
|
-
>
|
|
536
|
-
{
|
|
537
|
-
modifiers.map(this.renderOption)
|
|
538
|
-
}
|
|
539
|
-
</Select>
|
|
540
|
-
<span className='mg1x'>+</span>
|
|
541
|
-
<Select
|
|
542
|
-
value={key}
|
|
543
|
-
className='width100'
|
|
544
|
-
onChange={this.handleChangeKey}
|
|
545
|
-
popupMatchSelectWidth={false}
|
|
546
|
-
showSearch
|
|
547
|
-
>
|
|
548
|
-
{
|
|
549
|
-
keys.map(this.renderOption)
|
|
550
|
-
}
|
|
551
|
-
</Select>
|
|
552
|
-
</div>
|
|
496
|
+
<HotkeySetting
|
|
497
|
+
{...hotkeyProps}
|
|
498
|
+
/>
|
|
553
499
|
<div className='pd1b'>{e('onStartBookmarks')}</div>
|
|
554
500
|
<div className='pd2b'>
|
|
555
501
|
<StartSession
|
|
@@ -37,7 +37,7 @@ export function getKeyCharacter (code = '') {
|
|
|
37
37
|
}
|
|
38
38
|
if (code.startsWith('Key') && code.length === 4) {
|
|
39
39
|
return code[3].toLowerCase()
|
|
40
|
-
} else if (code.startsWith('Digit') && code.length ===
|
|
40
|
+
} else if (code.startsWith('Digit') && code.length === 6) {
|
|
41
41
|
return code[5]
|
|
42
42
|
} else {
|
|
43
43
|
return mapping[code] || code
|
|
@@ -139,6 +139,11 @@ class ShortcutControl extends React.PureComponent {
|
|
|
139
139
|
window.store.reloadTab()
|
|
140
140
|
}, 500)
|
|
141
141
|
|
|
142
|
+
reloadAllShortcut = throttle((e) => {
|
|
143
|
+
e.stopPropagation()
|
|
144
|
+
window.store.reloadAllTabs()
|
|
145
|
+
}, 500)
|
|
146
|
+
|
|
142
147
|
cloneToNextLayoutShortcut = throttle((e) => {
|
|
143
148
|
e.stopPropagation()
|
|
144
149
|
window.store.cloneToNextLayout()
|
|
@@ -106,6 +106,8 @@ export default class ShortcutEdit extends PureComponent {
|
|
|
106
106
|
altKey,
|
|
107
107
|
wheelDeltaY
|
|
108
108
|
} = e
|
|
109
|
+
e.preventDefault()
|
|
110
|
+
e.stopPropagation()
|
|
109
111
|
const codeName = e instanceof window.WheelEvent
|
|
110
112
|
? (wheelDeltaY > 0 ? 'mouseWheelUp' : 'mouseWheelDown')
|
|
111
113
|
: code
|
|
@@ -201,6 +203,7 @@ export default class ShortcutEdit extends PureComponent {
|
|
|
201
203
|
<Input
|
|
202
204
|
addonAfter={this.renderAfter()}
|
|
203
205
|
value={shortcut}
|
|
206
|
+
className='shortcut-input'
|
|
204
207
|
/>
|
|
205
208
|
</div>
|
|
206
209
|
)
|
|
@@ -44,6 +44,11 @@ function buildConfigForSearch (config) {
|
|
|
44
44
|
}, {})
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
function isInAntdInput () {
|
|
48
|
+
const activeElement = document.activeElement
|
|
49
|
+
return activeElement && activeElement.classList.contains('shortcut-input')
|
|
50
|
+
}
|
|
51
|
+
|
|
47
52
|
export function shortcutExtend (Cls) {
|
|
48
53
|
Cls.prototype.handleKeyboardEvent = function (event) {
|
|
49
54
|
const {
|
|
@@ -57,6 +62,9 @@ export function shortcutExtend (Cls) {
|
|
|
57
62
|
type,
|
|
58
63
|
key
|
|
59
64
|
} = event
|
|
65
|
+
if (isInAntdInput()) {
|
|
66
|
+
return
|
|
67
|
+
}
|
|
60
68
|
if (this.cmdAddon) {
|
|
61
69
|
this.cmdAddon.handleKey(event)
|
|
62
70
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { isMacJs as isMac } from '../../common/constants.js'
|
|
2
|
+
import shortcutsDefaultsGen from './shortcuts-defaults'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get keys taken data for shortcut conflict checking
|
|
6
|
+
* @returns {object} Object with shortcut strings as keys and true as values
|
|
7
|
+
*/
|
|
8
|
+
export function getKeysTakenData () {
|
|
9
|
+
const { store } = window
|
|
10
|
+
const { config, quickCommands } = store
|
|
11
|
+
const { shortcuts = {} } = config
|
|
12
|
+
|
|
13
|
+
// Get shortcuts defaults
|
|
14
|
+
const shortcutsDefaults = shortcutsDefaultsGen()
|
|
15
|
+
|
|
16
|
+
// Gather system shortcuts
|
|
17
|
+
const systemShortcuts = shortcutsDefaults.reduce((p, k) => {
|
|
18
|
+
const propName = isMac ? 'shortcutMac' : 'shortcut'
|
|
19
|
+
const name = k.name + '_' + propName
|
|
20
|
+
const vv = k.readonly ? k[propName] : (shortcuts[name] || k[propName])
|
|
21
|
+
if (!vv) return p
|
|
22
|
+
|
|
23
|
+
const v = vv
|
|
24
|
+
.split(',')
|
|
25
|
+
.map(f => f.trim())
|
|
26
|
+
.reduce((p, k) => ({
|
|
27
|
+
...p,
|
|
28
|
+
[k]: true
|
|
29
|
+
}), {})
|
|
30
|
+
return {
|
|
31
|
+
...p,
|
|
32
|
+
...v
|
|
33
|
+
}
|
|
34
|
+
}, {})
|
|
35
|
+
|
|
36
|
+
// Gather quick command shortcuts
|
|
37
|
+
const quickCommandShortcuts = quickCommands.reduce((acc, command) => {
|
|
38
|
+
if (command.shortcut) {
|
|
39
|
+
acc[command.shortcut] = true
|
|
40
|
+
}
|
|
41
|
+
return acc
|
|
42
|
+
}, {})
|
|
43
|
+
|
|
44
|
+
// Combine system shortcuts and quick command shortcuts
|
|
45
|
+
return {
|
|
46
|
+
...systemShortcuts,
|
|
47
|
+
...quickCommandShortcuts
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -6,9 +6,10 @@ import {
|
|
|
6
6
|
Table,
|
|
7
7
|
Button
|
|
8
8
|
} from 'antd'
|
|
9
|
+
import { isMacJs as isMac } from '../../common/constants.js'
|
|
9
10
|
import {
|
|
10
|
-
|
|
11
|
-
} from '
|
|
11
|
+
getKeysTakenData
|
|
12
|
+
} from './shortcut-utils.js'
|
|
12
13
|
|
|
13
14
|
const e = window.translate
|
|
14
15
|
const shortcutsDefaults = shortcutsDefaultsGen()
|
|
@@ -45,43 +46,6 @@ export default class Shortcuts extends PureComponent {
|
|
|
45
46
|
})
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
getKeysTakenData = () => {
|
|
49
|
-
const { config, quickCommands = [] } = this.props
|
|
50
|
-
const { shortcuts = {} } = config
|
|
51
|
-
|
|
52
|
-
// Gather system shortcuts
|
|
53
|
-
const systemShortcuts = shortcutsDefaults.reduce((p, k) => {
|
|
54
|
-
const propName = isMac ? 'shortcutMac' : 'shortcut'
|
|
55
|
-
const name = k.name + '_' + propName
|
|
56
|
-
const vv = k.readonly ? k[propName] : (shortcuts[name] || k[propName])
|
|
57
|
-
const v = vv
|
|
58
|
-
.split(',')
|
|
59
|
-
.map(f => f.trim())
|
|
60
|
-
.reduce((p, k) => ({
|
|
61
|
-
...p,
|
|
62
|
-
[k]: true
|
|
63
|
-
}), {})
|
|
64
|
-
return {
|
|
65
|
-
...p,
|
|
66
|
-
...v
|
|
67
|
-
}
|
|
68
|
-
}, {})
|
|
69
|
-
|
|
70
|
-
// Gather quick command shortcuts
|
|
71
|
-
const quickCommandShortcuts = quickCommands.reduce((acc, command) => {
|
|
72
|
-
if (command.shortcut) {
|
|
73
|
-
acc[command.shortcut] = true
|
|
74
|
-
}
|
|
75
|
-
return acc
|
|
76
|
-
}, {})
|
|
77
|
-
|
|
78
|
-
// Combine system shortcuts and quick command shortcuts
|
|
79
|
-
return {
|
|
80
|
-
...systemShortcuts,
|
|
81
|
-
...quickCommandShortcuts
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
49
|
render () {
|
|
86
50
|
const columns = [
|
|
87
51
|
{
|
|
@@ -124,7 +88,7 @@ export default class Shortcuts extends PureComponent {
|
|
|
124
88
|
return (
|
|
125
89
|
<ShortcutEdit
|
|
126
90
|
data={inst}
|
|
127
|
-
keysTaken={
|
|
91
|
+
keysTaken={getKeysTakenData()}
|
|
128
92
|
updateConfig={this.updateConfig}
|
|
129
93
|
/>
|
|
130
94
|
)
|
|
@@ -190,6 +190,10 @@ class Tab extends Component {
|
|
|
190
190
|
window.store.reloadTab(this.props.tab.id)
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
handleReloadAll = () => {
|
|
194
|
+
window.store.reloadAllTabs()
|
|
195
|
+
}
|
|
196
|
+
|
|
193
197
|
onDragEnd = e => {
|
|
194
198
|
removeClass(this.tabRef.current, onDragCls)
|
|
195
199
|
this.clearCls()
|
|
@@ -260,6 +264,7 @@ class Tab extends Component {
|
|
|
260
264
|
const closeShortcut = this.getShortcut('app_closeCurrentTab')
|
|
261
265
|
const cloneToNextShortcut = this.getShortcut('app_cloneToNextLayout')
|
|
262
266
|
const duplicateShortcut = this.getShortcut('app_duplicateTab')
|
|
267
|
+
const reloadAllShortcut = this.getShortcut('app_reloadAll')
|
|
263
268
|
|
|
264
269
|
const x = [
|
|
265
270
|
{
|
|
@@ -305,6 +310,12 @@ class Tab extends Component {
|
|
|
305
310
|
icon: <iconsMap.ReloadOutlined />,
|
|
306
311
|
label: e('reload'),
|
|
307
312
|
extra: reloadShortcut
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
key: 'handleReloadAll',
|
|
316
|
+
icon: <iconsMap.ReloadOutlined />,
|
|
317
|
+
label: e('reloadAll'),
|
|
318
|
+
extra: reloadAllShortcut
|
|
308
319
|
}
|
|
309
320
|
].filter(Boolean)
|
|
310
321
|
return x
|
package/client/store/tab.js
CHANGED
|
@@ -124,6 +124,17 @@ export default Store => {
|
|
|
124
124
|
}, 0)
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
Store.prototype.reloadAllTabs = function () {
|
|
128
|
+
const { store } = window
|
|
129
|
+
const { tabs } = store
|
|
130
|
+
// Reload all tabs with a small delay between each to avoid conflicts
|
|
131
|
+
tabs.forEach((tab, index) => {
|
|
132
|
+
setTimeout(() => {
|
|
133
|
+
store.reloadTab(tab.id)
|
|
134
|
+
}, index * 100) // 100ms delay between each reload
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
127
138
|
Store.prototype.duplicateTab = function (tabId) {
|
|
128
139
|
const { store } = window
|
|
129
140
|
const { tabs } = store
|