@electerm/electerm-react 1.35.0 → 1.36.1
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/common/constants.js +1 -0
- package/client/components/bookmark-form/color-picker.jsx +1 -0
- package/client/components/bookmark-form/local-form-ui.jsx +6 -2
- package/client/components/bookmark-form/telnet-form-ui.jsx +2 -0
- package/client/components/main/custom-css.jsx +1 -1
- package/client/components/session/session.jsx +1 -1
- package/client/components/setting-panel/keywords-form.jsx +124 -0
- package/client/components/setting-panel/{setting.jsx → setting-common.jsx} +2 -288
- package/client/components/setting-panel/setting-terminal.jsx +494 -0
- package/client/components/setting-panel/tab-settings.jsx +6 -2
- package/client/components/sftp/list-table-ui.jsx +3 -2
- package/client/components/shortcuts/shortcut-control.jsx +12 -3
- package/client/components/shortcuts/shortcut-editor.jsx +3 -3
- package/client/components/shortcuts/shortcut-handler.js +0 -1
- package/client/components/shortcuts/shortcuts-defaults.js +9 -4
- package/client/components/shortcuts/shortcuts.jsx +1 -1
- package/client/components/terminal/highlight-addon.js +63 -0
- package/client/components/terminal/index.jsx +16 -45
- package/client/store/index.js +9 -3
- package/client/store/setting.js +2 -1
- package/package.json +1 -1
- package/client/common/shortcuts-defaults.js +0 -51
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
import React, { Component } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
CodeOutlined,
|
|
4
|
+
LoadingOutlined
|
|
5
|
+
} from '@ant-design/icons'
|
|
6
|
+
import {
|
|
7
|
+
message,
|
|
8
|
+
Select,
|
|
9
|
+
Switch,
|
|
10
|
+
Input,
|
|
11
|
+
Upload,
|
|
12
|
+
InputNumber,
|
|
13
|
+
Button,
|
|
14
|
+
AutoComplete,
|
|
15
|
+
Tooltip
|
|
16
|
+
} from 'antd'
|
|
17
|
+
import deepCopy from 'json-deep-copy'
|
|
18
|
+
import {
|
|
19
|
+
noTerminalBgValue,
|
|
20
|
+
rendererTypes
|
|
21
|
+
} from '../../common/constants'
|
|
22
|
+
import defaultSettings from '../../common/default-setting'
|
|
23
|
+
import ShowItem from '../common/show-item'
|
|
24
|
+
import { osResolve } from '../../common/resolve'
|
|
25
|
+
import { isNumber, isNaN } from 'lodash-es'
|
|
26
|
+
import mapper from '../../common/auto-complete-data-mapper'
|
|
27
|
+
import KeywordForm from './keywords-form'
|
|
28
|
+
import HelpIcon from '../common/help-icon'
|
|
29
|
+
import './setting.styl'
|
|
30
|
+
|
|
31
|
+
const { Option } = Select
|
|
32
|
+
const { prefix } = window
|
|
33
|
+
const e = prefix('setting')
|
|
34
|
+
const s = prefix('ssh')
|
|
35
|
+
const p = prefix('sftp')
|
|
36
|
+
const t = prefix('terminalThemes')
|
|
37
|
+
const f = prefix('form')
|
|
38
|
+
|
|
39
|
+
export default class SettingTerminal extends Component {
|
|
40
|
+
state = {
|
|
41
|
+
ready: false
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
componentDidMount () {
|
|
45
|
+
this.timer = setTimeout(() => {
|
|
46
|
+
this.setState({
|
|
47
|
+
ready: true
|
|
48
|
+
})
|
|
49
|
+
}, 200)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
componentWillUnmount () {
|
|
53
|
+
clearTimeout(this.timer)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
handleResetAll = () => {
|
|
57
|
+
this.saveConfig(
|
|
58
|
+
deepCopy(defaultSettings)
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
onChangeValue = (value, name) => {
|
|
63
|
+
if (name === 'useSystemTitleBar') {
|
|
64
|
+
message.info(e('useSystemTitleBarTip'), 5)
|
|
65
|
+
}
|
|
66
|
+
this.saveConfig({
|
|
67
|
+
[name]: value
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
handleChangeFont = (values) => {
|
|
72
|
+
this.onChangeValue(
|
|
73
|
+
values.join(', '),
|
|
74
|
+
'fontFamily'
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
handleChangeCursorStyle = (cursorStyle) => {
|
|
79
|
+
this.onChangeValue(
|
|
80
|
+
cursorStyle,
|
|
81
|
+
'cursorStyle'
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
saveConfig = async (ext) => {
|
|
86
|
+
const { config } = this.props
|
|
87
|
+
if (ext.hotkey && ext.hotkey !== config.hotkey) {
|
|
88
|
+
const res = await window.pre.runGlobalAsync('changeHotkey', ext.hotkey)
|
|
89
|
+
if (!res) {
|
|
90
|
+
message.warning(e('hotkeyNotOk'))
|
|
91
|
+
delete ext.hotkey
|
|
92
|
+
} else {
|
|
93
|
+
message.success(e('saved'))
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
this.props.store.setConfig(ext)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
handleSubmitKeywords = (data) => {
|
|
100
|
+
return this.saveConfig(data)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
renderToggle = (name, extra = null) => {
|
|
104
|
+
const checked = !!this.props.config[name]
|
|
105
|
+
return (
|
|
106
|
+
<div className='pd2b' key={'rt' + name}>
|
|
107
|
+
<Switch
|
|
108
|
+
checked={checked}
|
|
109
|
+
checkedChildren={e(name)}
|
|
110
|
+
unCheckedChildren={e(name)}
|
|
111
|
+
onChange={v => this.onChangeValue(v, name)}
|
|
112
|
+
/>
|
|
113
|
+
{isNumber(extra) ? null : extra}
|
|
114
|
+
</div>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
renderNumber = (name, options, title = '', width = 136) => {
|
|
119
|
+
let value = this.props.config[name]
|
|
120
|
+
if (options.valueParser) {
|
|
121
|
+
value = options.valueParser(value)
|
|
122
|
+
}
|
|
123
|
+
const defaultValue = defaultSettings[name]
|
|
124
|
+
const {
|
|
125
|
+
step = 1,
|
|
126
|
+
min,
|
|
127
|
+
max,
|
|
128
|
+
cls,
|
|
129
|
+
onChange = (v) => {
|
|
130
|
+
this.onChangeValue(v, name)
|
|
131
|
+
}
|
|
132
|
+
} = options
|
|
133
|
+
const opts = {
|
|
134
|
+
step,
|
|
135
|
+
value,
|
|
136
|
+
min,
|
|
137
|
+
max,
|
|
138
|
+
onChange,
|
|
139
|
+
placeholder: defaultValue
|
|
140
|
+
}
|
|
141
|
+
if (title) {
|
|
142
|
+
opts.formatter = v => `${title}${options.extraDesc || ''}: ${v}`
|
|
143
|
+
opts.parser = (v) => {
|
|
144
|
+
let vv = isNumber(v)
|
|
145
|
+
? v
|
|
146
|
+
: Number(v.split(': ')[1], 10)
|
|
147
|
+
if (isNaN(vv)) {
|
|
148
|
+
vv = defaultValue
|
|
149
|
+
}
|
|
150
|
+
return vv
|
|
151
|
+
}
|
|
152
|
+
opts.style = {
|
|
153
|
+
width: width + 'px'
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return (
|
|
157
|
+
<div className={`pd2b ${cls || ''}`}>
|
|
158
|
+
<InputNumber
|
|
159
|
+
{...opts}
|
|
160
|
+
/>
|
|
161
|
+
</div>
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
renderText = (name, placeholder) => {
|
|
166
|
+
const value = this.props.config[name]
|
|
167
|
+
const defaultValue = defaultSettings[name]
|
|
168
|
+
const onChange = (e) => this.onChangeValue(e.target.value, name)
|
|
169
|
+
return (
|
|
170
|
+
<div className='pd2b'>
|
|
171
|
+
<Input
|
|
172
|
+
value={value}
|
|
173
|
+
onChange={onChange}
|
|
174
|
+
placeholder={placeholder || defaultValue}
|
|
175
|
+
/>
|
|
176
|
+
</div>
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
renderBgOption = item => {
|
|
181
|
+
return {
|
|
182
|
+
value: item.value,
|
|
183
|
+
label: item.desc
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
renderTerminalBgSelect = (name) => {
|
|
188
|
+
const value = this.props.config[name]
|
|
189
|
+
const defaultValue = defaultSettings[name]
|
|
190
|
+
const onChange = (v) => this.onChangeValue(v, name)
|
|
191
|
+
const after = (
|
|
192
|
+
<Upload
|
|
193
|
+
beforeUpload={(file) => {
|
|
194
|
+
this.onChangeValue(file.path, name)
|
|
195
|
+
return false
|
|
196
|
+
}}
|
|
197
|
+
showUploadList={false}
|
|
198
|
+
>
|
|
199
|
+
<span>{e('chooseFile')}</span>
|
|
200
|
+
</Upload>
|
|
201
|
+
)
|
|
202
|
+
const dataSource = [
|
|
203
|
+
{
|
|
204
|
+
value: '',
|
|
205
|
+
desc: t('default')
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
value: noTerminalBgValue,
|
|
209
|
+
desc: e('noTerminalBg')
|
|
210
|
+
}
|
|
211
|
+
]
|
|
212
|
+
const numberOpts = { step: 0.05, min: 0, max: 1, cls: 'bg-img-setting' }
|
|
213
|
+
|
|
214
|
+
const renderFilter = () => {
|
|
215
|
+
if (this.props.config[name] === noTerminalBgValue) return
|
|
216
|
+
|
|
217
|
+
return (
|
|
218
|
+
<div>
|
|
219
|
+
{
|
|
220
|
+
this.renderNumber(
|
|
221
|
+
'terminalBackgroundFilterOpacity',
|
|
222
|
+
numberOpts,
|
|
223
|
+
e('Opacity')
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
{
|
|
227
|
+
this.renderNumber(
|
|
228
|
+
'terminalBackgroundFilterBlur',
|
|
229
|
+
{ ...numberOpts, min: 0, max: 50, step: 0.5 },
|
|
230
|
+
e('Blur')
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
{
|
|
234
|
+
this.renderNumber(
|
|
235
|
+
'terminalBackgroundFilterBrightness',
|
|
236
|
+
{ ...numberOpts, min: 0, max: 10, step: 0.1 },
|
|
237
|
+
e('Brightness')
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
{
|
|
241
|
+
this.renderNumber(
|
|
242
|
+
'terminalBackgroundFilterGrayscale',
|
|
243
|
+
numberOpts,
|
|
244
|
+
e('Grayscale')
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
{
|
|
248
|
+
this.renderNumber(
|
|
249
|
+
'terminalBackgroundFilterContrast',
|
|
250
|
+
{ ...numberOpts, min: 0, max: 10, step: 0.1 },
|
|
251
|
+
e('Contrast')
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
</div>
|
|
255
|
+
)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return (
|
|
259
|
+
<div className='pd2b'>
|
|
260
|
+
<div className='pd1b'>
|
|
261
|
+
<Tooltip
|
|
262
|
+
title='eg: https://xx.com/xx.png or /path/to/xx.png'
|
|
263
|
+
>
|
|
264
|
+
<AutoComplete
|
|
265
|
+
value={value}
|
|
266
|
+
onChange={onChange}
|
|
267
|
+
placeholder={defaultValue}
|
|
268
|
+
className='width-100'
|
|
269
|
+
options={dataSource.map(this.renderBgOption)}
|
|
270
|
+
>
|
|
271
|
+
<Input
|
|
272
|
+
addonAfter={after}
|
|
273
|
+
/>
|
|
274
|
+
</AutoComplete>
|
|
275
|
+
</Tooltip>
|
|
276
|
+
</div>
|
|
277
|
+
|
|
278
|
+
{
|
|
279
|
+
renderFilter()
|
|
280
|
+
}
|
|
281
|
+
</div>
|
|
282
|
+
)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
renderReset = () => {
|
|
286
|
+
return (
|
|
287
|
+
<div className='pd1b pd1t'>
|
|
288
|
+
<Button
|
|
289
|
+
onClick={this.handleResetAll}
|
|
290
|
+
>
|
|
291
|
+
{e('resetAllToDefault')}
|
|
292
|
+
</Button>
|
|
293
|
+
</div>
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
renderDefaultTerminalType = () => {
|
|
298
|
+
const opts = this.props.config.terminalTypes.map(mapper)
|
|
299
|
+
return (
|
|
300
|
+
<AutoComplete
|
|
301
|
+
options={opts}
|
|
302
|
+
style={{
|
|
303
|
+
width: '200px'
|
|
304
|
+
}}
|
|
305
|
+
value={this.props.config.terminalType}
|
|
306
|
+
onChange={(v) => this.onChangeValue(v, 'terminalType')}
|
|
307
|
+
/>
|
|
308
|
+
)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
renderCursorStyleSelect = () => {
|
|
312
|
+
const {
|
|
313
|
+
cursorStyle = 'block'
|
|
314
|
+
} = this.props.config
|
|
315
|
+
const sets = [
|
|
316
|
+
{
|
|
317
|
+
id: 'block',
|
|
318
|
+
title: '▊'
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
id: 'underline',
|
|
322
|
+
title: '_'
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
id: 'bar',
|
|
326
|
+
title: '|'
|
|
327
|
+
}
|
|
328
|
+
]
|
|
329
|
+
const props = {
|
|
330
|
+
onChange: this.handleChangeCursorStyle,
|
|
331
|
+
value: cursorStyle,
|
|
332
|
+
style: {
|
|
333
|
+
width: '100px'
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return (
|
|
337
|
+
<div className='pd1b'>
|
|
338
|
+
<span className='inline-title mg1r'>{e('cursorStyle')}</span>
|
|
339
|
+
<Select
|
|
340
|
+
{...props}
|
|
341
|
+
showSearch
|
|
342
|
+
>
|
|
343
|
+
{
|
|
344
|
+
sets.map(f => {
|
|
345
|
+
return (
|
|
346
|
+
<Option value={f.id} key={f.id}>
|
|
347
|
+
<b>{f.title}</b>
|
|
348
|
+
</Option>
|
|
349
|
+
)
|
|
350
|
+
})
|
|
351
|
+
}
|
|
352
|
+
</Select>
|
|
353
|
+
</div>
|
|
354
|
+
)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
renderFontFamily = () => {
|
|
358
|
+
const { fonts } = this.props.store
|
|
359
|
+
const { fontFamily } = this.props.config
|
|
360
|
+
const props = {
|
|
361
|
+
mode: 'multiple',
|
|
362
|
+
onChange: this.handleChangeFont,
|
|
363
|
+
value: fontFamily.split(/, */g)
|
|
364
|
+
}
|
|
365
|
+
return (
|
|
366
|
+
<Select
|
|
367
|
+
{...props}
|
|
368
|
+
showSearch
|
|
369
|
+
>
|
|
370
|
+
{
|
|
371
|
+
fonts.map(f => {
|
|
372
|
+
return (
|
|
373
|
+
<Option value={f} key={f}>
|
|
374
|
+
<span style={{
|
|
375
|
+
fontFamily: f
|
|
376
|
+
}}
|
|
377
|
+
>{f}
|
|
378
|
+
</span>
|
|
379
|
+
</Option>
|
|
380
|
+
)
|
|
381
|
+
})
|
|
382
|
+
}
|
|
383
|
+
</Select>
|
|
384
|
+
)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
render () {
|
|
388
|
+
const { ready } = this.state
|
|
389
|
+
if (!ready) {
|
|
390
|
+
return (
|
|
391
|
+
<div className='pd3 aligncenter'>
|
|
392
|
+
<LoadingOutlined />
|
|
393
|
+
</div>
|
|
394
|
+
)
|
|
395
|
+
}
|
|
396
|
+
const {
|
|
397
|
+
rendererType,
|
|
398
|
+
keywords = [{ color: 'red' }]
|
|
399
|
+
} = this.props.config
|
|
400
|
+
const {
|
|
401
|
+
appPath
|
|
402
|
+
} = this.props.store
|
|
403
|
+
const ps = {
|
|
404
|
+
formData: {
|
|
405
|
+
keywords
|
|
406
|
+
},
|
|
407
|
+
submit: this.handleSubmitKeywords
|
|
408
|
+
}
|
|
409
|
+
const terminalLogPath = appPath ? osResolve(appPath, 'electerm', 'session_logs') : window.et.sessionLogPath
|
|
410
|
+
return (
|
|
411
|
+
<div className='form-wrap pd1y pd2x'>
|
|
412
|
+
<div className='pd1y font16 bold'>
|
|
413
|
+
<CodeOutlined className='mg1r' />
|
|
414
|
+
{s('terminal')} {e('settings')}
|
|
415
|
+
</div>
|
|
416
|
+
{
|
|
417
|
+
this.renderNumber('scrollback', {
|
|
418
|
+
step: 200,
|
|
419
|
+
min: 1000
|
|
420
|
+
}, e('scrollBackDesc'), 400)
|
|
421
|
+
}
|
|
422
|
+
<div className='pd2b'>
|
|
423
|
+
<span className='inline-title mg1r'>{e('rendererType')}</span>
|
|
424
|
+
<Select
|
|
425
|
+
onChange={v => this.onChangeValue(v, 'rendererType')}
|
|
426
|
+
value={rendererType}
|
|
427
|
+
popupMatchSelectWidth={false}
|
|
428
|
+
>
|
|
429
|
+
{
|
|
430
|
+
Object.keys(rendererTypes).map(id => {
|
|
431
|
+
return (
|
|
432
|
+
<Option key={id} value={id}>{id}</Option>
|
|
433
|
+
)
|
|
434
|
+
})
|
|
435
|
+
}
|
|
436
|
+
</Select>
|
|
437
|
+
</div>
|
|
438
|
+
{
|
|
439
|
+
this.renderNumber('fontSize', {
|
|
440
|
+
step: 1,
|
|
441
|
+
min: 9
|
|
442
|
+
}, `${t('default')} ${e('fontSize')}`, 400)
|
|
443
|
+
}
|
|
444
|
+
<div className='pd2b'>
|
|
445
|
+
<span className='inline-title mg1r'>{t('default')} {e('fontFamily')}</span>
|
|
446
|
+
{
|
|
447
|
+
this.renderFontFamily()
|
|
448
|
+
}
|
|
449
|
+
</div>
|
|
450
|
+
<div className='pd2b'>
|
|
451
|
+
<div className='pd1b'>
|
|
452
|
+
<span className='inline-title mg1r'>{f('keywordsHighlight')}</span>
|
|
453
|
+
<HelpIcon
|
|
454
|
+
title={f('supportRegexp')}
|
|
455
|
+
/>
|
|
456
|
+
</div>
|
|
457
|
+
<KeywordForm
|
|
458
|
+
{...ps}
|
|
459
|
+
/>
|
|
460
|
+
</div>
|
|
461
|
+
<div className='pd2b'>
|
|
462
|
+
<span className='inline-title mg1r'>{e('defaultTerminalType')}</span>
|
|
463
|
+
{
|
|
464
|
+
this.renderDefaultTerminalType()
|
|
465
|
+
}
|
|
466
|
+
</div>
|
|
467
|
+
<div className='pd1b'>{e('terminalBackgroundImage')}</div>
|
|
468
|
+
{
|
|
469
|
+
this.renderTerminalBgSelect('terminalBackgroundImagePath')
|
|
470
|
+
}
|
|
471
|
+
<div className='pd1b'>{e('terminalWordSeparator')}</div>
|
|
472
|
+
{
|
|
473
|
+
this.renderText('terminalWordSeparator', e('terminalWordSeparator'))
|
|
474
|
+
}
|
|
475
|
+
{
|
|
476
|
+
this.renderCursorStyleSelect()
|
|
477
|
+
}
|
|
478
|
+
{
|
|
479
|
+
[
|
|
480
|
+
'cursorBlink',
|
|
481
|
+
'rightClickSelectsWord',
|
|
482
|
+
'pasteWhenContextMenu',
|
|
483
|
+
'copyWhenSelect',
|
|
484
|
+
'ctrlOrMetaOpenTerminalLink'
|
|
485
|
+
].map(this.renderToggle)
|
|
486
|
+
}
|
|
487
|
+
{this.renderToggle('saveTerminalLogToFile', (
|
|
488
|
+
<ShowItem to={terminalLogPath} className='mg1l'>{p('open')}</ShowItem>
|
|
489
|
+
))}
|
|
490
|
+
{this.renderReset()}
|
|
491
|
+
</div>
|
|
492
|
+
)
|
|
493
|
+
}
|
|
494
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import SettingCommon from './setting-common'
|
|
2
|
+
import SettingTerminal from './setting-terminal'
|
|
2
3
|
import SettingCol from './col'
|
|
3
4
|
import SyncSetting from '../setting-sync/setting-sync'
|
|
4
5
|
import Shortcuts from '../shortcuts/shortcuts'
|
|
@@ -6,6 +7,7 @@ import List from './list'
|
|
|
6
7
|
import {
|
|
7
8
|
settingMap,
|
|
8
9
|
settingSyncId,
|
|
10
|
+
settingTerminalId,
|
|
9
11
|
settingShortcutsId
|
|
10
12
|
} from '../../common/constants'
|
|
11
13
|
|
|
@@ -25,10 +27,12 @@ export default function TabSettings (props) {
|
|
|
25
27
|
const sid = settingItem.id
|
|
26
28
|
if (sid === settingSyncId) {
|
|
27
29
|
elem = <SyncSetting store={store} />
|
|
30
|
+
} else if (sid === settingTerminalId) {
|
|
31
|
+
elem = <SettingTerminal {...listProps} config={store.config} />
|
|
28
32
|
} else if (sid === settingShortcutsId) {
|
|
29
33
|
elem = <Shortcuts store={store} />
|
|
30
34
|
} else {
|
|
31
|
-
elem = <
|
|
35
|
+
elem = <SettingCommon {...listProps} config={store.config} />
|
|
32
36
|
}
|
|
33
37
|
return (
|
|
34
38
|
<div
|
|
@@ -94,7 +94,7 @@ export default class FileListTable extends React.Component {
|
|
|
94
94
|
style: {
|
|
95
95
|
width: w + 'px',
|
|
96
96
|
left: (w * i) + 'px',
|
|
97
|
-
zIndex: 3 + i
|
|
97
|
+
zIndex: 3 + i * 2
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
})
|
|
@@ -110,7 +110,8 @@ export default class FileListTable extends React.Component {
|
|
|
110
110
|
nextProp: properties[i + 1].name,
|
|
111
111
|
style: {
|
|
112
112
|
left: (w * (i + 1) - (splitDraggerWidth / 2)) + 'px',
|
|
113
|
-
width: splitDraggerWidth + 'px'
|
|
113
|
+
width: splitDraggerWidth + 'px',
|
|
114
|
+
zIndex: 4 + i * 2
|
|
114
115
|
}
|
|
115
116
|
}
|
|
116
117
|
]
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import React from 'react'
|
|
7
7
|
import { shortcutExtend } from './shortcut-handler.js'
|
|
8
|
+
import { throttle } from 'lodash-es'
|
|
8
9
|
|
|
9
10
|
class ShortcutControl extends React.PureComponent {
|
|
10
11
|
componentDidMount () {
|
|
@@ -27,10 +28,18 @@ class ShortcutControl extends React.PureComponent {
|
|
|
27
28
|
window.store.onNewSsh()
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
togglefullscreenShortcut = (e) => {
|
|
31
|
+
togglefullscreenShortcut = throttle((e) => {
|
|
31
32
|
e.stopPropagation()
|
|
32
|
-
document.querySelector('.term-fullscreen-control')
|
|
33
|
-
|
|
33
|
+
const x = document.querySelector('.term-fullscreen-control') ||
|
|
34
|
+
document.querySelector('.term-fullscreen-control1')
|
|
35
|
+
x && x.click()
|
|
36
|
+
}, 300)
|
|
37
|
+
|
|
38
|
+
splitShortcut = throttle((e) => {
|
|
39
|
+
e.stopPropagation()
|
|
40
|
+
const x = document.querySelector('.icon-split')
|
|
41
|
+
x && x.click()
|
|
42
|
+
}, 300)
|
|
34
43
|
|
|
35
44
|
zoominShortcut = (e) => {
|
|
36
45
|
e.stopPropagation()
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
CheckOutlined,
|
|
10
10
|
CloseOutlined
|
|
11
11
|
} from '@ant-design/icons'
|
|
12
|
-
import {
|
|
12
|
+
import { throttle } from 'lodash-es'
|
|
13
13
|
import { getKeyCharacter } from './get-key-char.js'
|
|
14
14
|
|
|
15
15
|
export default class ShortcutEdit extends PureComponent {
|
|
@@ -83,14 +83,14 @@ export default class ShortcutEdit extends PureComponent {
|
|
|
83
83
|
return 'shortcut-control-' + index
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
warnCtrolKey =
|
|
86
|
+
warnCtrolKey = throttle(() => {
|
|
87
87
|
message.info(
|
|
88
88
|
'Must have one of Ctrl or Shift or Alt or Meta key',
|
|
89
89
|
undefined
|
|
90
90
|
)
|
|
91
91
|
}, 3000)
|
|
92
92
|
|
|
93
|
-
warnExist =
|
|
93
|
+
warnExist = throttle(() => {
|
|
94
94
|
message.info(
|
|
95
95
|
'Shortcut already exists',
|
|
96
96
|
undefined
|
|
@@ -2,13 +2,13 @@ export default () => {
|
|
|
2
2
|
return [
|
|
3
3
|
{
|
|
4
4
|
name: 'app_closeCurrentTab',
|
|
5
|
-
shortcut: '
|
|
6
|
-
shortcutMac: '
|
|
5
|
+
shortcut: 'alt+w',
|
|
6
|
+
shortcutMac: 'alt+w'
|
|
7
7
|
},
|
|
8
8
|
{
|
|
9
9
|
name: 'app_newBookmark',
|
|
10
|
-
shortcut: 'ctrl+
|
|
11
|
-
shortcutMac: 'meta+
|
|
10
|
+
shortcut: 'ctrl+n',
|
|
11
|
+
shortcutMac: 'meta+n'
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
name: 'app_togglefullscreen',
|
|
@@ -35,6 +35,11 @@ export default () => {
|
|
|
35
35
|
shortcut: 'ctrl+tab',
|
|
36
36
|
shortcutMac: 'ctrl+tab'
|
|
37
37
|
},
|
|
38
|
+
{
|
|
39
|
+
name: 'terminal_split',
|
|
40
|
+
shortcut: 'ctrl+/',
|
|
41
|
+
shortcutMac: 'meta+/'
|
|
42
|
+
},
|
|
38
43
|
{
|
|
39
44
|
name: 'terminal_clear',
|
|
40
45
|
shortcut: 'ctrl+l,ctrl+shift+l',
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export class KeywordHighlighterAddon {
|
|
2
|
+
constructor (keywords) {
|
|
3
|
+
this.keywords = keywords
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
escape = str => {
|
|
7
|
+
return str.replace(/\\x1B/g, '\\x1B')
|
|
8
|
+
.replace(/\033/g, '\\033')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
colorize = (color) => {
|
|
12
|
+
// Use a switch statement to map color names to ANSI codes
|
|
13
|
+
switch (color) {
|
|
14
|
+
case 'green':
|
|
15
|
+
return '\u001b[32m$&\u001b[0m'
|
|
16
|
+
case 'yellow':
|
|
17
|
+
return '\u001b[33m$&\u001b[0m'
|
|
18
|
+
case 'blue':
|
|
19
|
+
return '\u001b[34m$&\u001b[0m'
|
|
20
|
+
case 'magenta':
|
|
21
|
+
return '\u001b[35m$&\u001b[0m'
|
|
22
|
+
case 'cyan':
|
|
23
|
+
return '\u001b[36m$&\u001b[0m'
|
|
24
|
+
case 'white':
|
|
25
|
+
return '\u001b[37m$&\u001b[0m'
|
|
26
|
+
default:
|
|
27
|
+
return '\u001b[31m$&\u001b[0m'
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
highlightKeywords = (text) => {
|
|
32
|
+
console.log('this.keywords', this.keywords)
|
|
33
|
+
for (const obj of this.keywords) {
|
|
34
|
+
const {
|
|
35
|
+
keyword,
|
|
36
|
+
color = 'red'
|
|
37
|
+
} = obj || {}
|
|
38
|
+
if (keyword) {
|
|
39
|
+
const regex = new RegExp(`(${keyword})`, 'gi')
|
|
40
|
+
text = text.replace(regex, this.colorize(color))
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return text
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
activate (terminal) {
|
|
47
|
+
this.terminal = terminal
|
|
48
|
+
// Override the write method to automatically highlight keywords
|
|
49
|
+
const originalWrite = terminal.write
|
|
50
|
+
terminal.write = (data) => {
|
|
51
|
+
originalWrite.call(
|
|
52
|
+
terminal,
|
|
53
|
+
terminal.displayRaw ? this.escape(data) : this.highlightKeywords(data)
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
this.originalWrite = originalWrite
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
dispose () {
|
|
60
|
+
// Restore the original write method when disposing the addon
|
|
61
|
+
this.terminal.write = this.originalWrite
|
|
62
|
+
}
|
|
63
|
+
}
|