@electerm/electerm-react 1.34.68 → 1.35.6

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 (45) hide show
  1. package/client/common/constants.js +3 -1
  2. package/client/common/key-pressed.js +1 -1
  3. package/client/common/rand-hex-color.js +28 -0
  4. package/client/components/batch-op/batch-op.jsx +6 -2
  5. package/client/components/bookmark-form/color-picker-item.jsx +14 -0
  6. package/client/components/bookmark-form/color-picker.jsx +90 -0
  7. package/client/components/bookmark-form/color-picker.styl +20 -0
  8. package/client/components/bookmark-form/form-ssh-common.jsx +4 -0
  9. package/client/components/bookmark-form/hex-input.jsx +39 -0
  10. package/client/components/bookmark-form/local-form-ui.jsx +1 -0
  11. package/client/components/bookmark-form/serial-form-ui.jsx +12 -8
  12. package/client/components/bookmark-form/ssh-form-ui.jsx +3 -0
  13. package/client/components/bookmark-form/telnet-form-ui.jsx +1 -0
  14. package/client/components/bookmark-form/use-ui.jsx +11 -1
  15. package/client/components/context-menu/context-menu.styl +1 -1
  16. package/client/components/context-menu/menu-btn.jsx +6 -3
  17. package/client/components/main/custom-css.jsx +28 -0
  18. package/client/components/main/main.jsx +5 -2
  19. package/client/components/session/session.jsx +11 -35
  20. package/client/components/session/sessions.jsx +15 -15
  21. package/client/components/setting-panel/setting.jsx +20 -32
  22. package/client/components/setting-panel/tab-settings.jsx +13 -10
  23. package/client/components/sftp/transfer-tag.jsx +2 -2
  24. package/client/components/sftp/transport-action.jsx +1 -0
  25. package/client/components/shortcuts/get-key-char.js +45 -0
  26. package/client/components/shortcuts/shortcut-control.jsx +72 -0
  27. package/client/components/shortcuts/shortcut-editor.jsx +194 -0
  28. package/client/components/shortcuts/shortcut-handler.js +75 -0
  29. package/client/components/shortcuts/shortcut.styl +0 -0
  30. package/client/components/shortcuts/shortcuts-defaults.js +92 -0
  31. package/client/components/shortcuts/shortcuts.jsx +166 -0
  32. package/client/components/sidebar/index.jsx +1 -1
  33. package/client/components/sidebar/transfer-history-modal.jsx +14 -2
  34. package/client/components/tabs/index.jsx +0 -25
  35. package/client/components/tabs/tab.jsx +6 -5
  36. package/client/components/terminal/index.jsx +95 -112
  37. package/client/components/terminal/term-search.jsx +9 -21
  38. package/client/components/terminal-theme/index.jsx +4 -3
  39. package/client/store/index.js +17 -2
  40. package/client/store/init-state.js +1 -8
  41. package/client/store/item.js +3 -0
  42. package/client/store/session.js +0 -22
  43. package/client/store/watch.js +3 -1
  44. package/client/views/index.pug +2 -0
  45. package/package.json +1 -1
@@ -34,7 +34,6 @@ import createEditLangLink from '../../common/create-lang-edit-link'
34
34
  import mapper from '../../common/auto-complete-data-mapper'
35
35
  import StartSession from './start-session-select'
36
36
  import HelpIcon from '../common/help-icon'
37
- import fs from '../../common/fs'
38
37
  import delay from '../../common/wait.js'
39
38
  import './setting.styl'
40
39
 
@@ -186,6 +185,10 @@ export default class Setting extends Component {
186
185
  this.props.store.setTheme(id)
187
186
  }
188
187
 
188
+ handleCustomCss = (e) => {
189
+ this.onChangeValue(e.target.value, 'customCss')
190
+ }
191
+
189
192
  onChangeValue = (value, name) => {
190
193
  if (name === 'useSystemTitleBar') {
191
194
  message.info(e('useSystemTitleBarTip'), 5)
@@ -213,21 +216,18 @@ export default class Setting extends Component {
213
216
  )
214
217
  }
215
218
 
216
- saveConfig = async (_ext) => {
217
- const config = deepCopy(this.props.config)
218
- const ext = deepCopy(_ext)
219
- const update = Object.assign({}, config, deepCopy(_ext))
219
+ saveConfig = async (ext) => {
220
+ const { config } = this.props
220
221
  if (ext.hotkey && ext.hotkey !== config.hotkey) {
221
222
  const res = await window.pre.runGlobalAsync('changeHotkey', ext.hotkey)
222
223
  if (!res) {
223
224
  message.warning(e('hotkeyNotOk'))
224
- update.config.hotkey = config.hotkey
225
- ext.hotkey = config.hotkey
225
+ delete ext.hotkey
226
226
  } else {
227
227
  message.success(e('saved'))
228
228
  }
229
229
  }
230
- this.props.store.setConfig(update)
230
+ this.props.store.setConfig(ext)
231
231
  }
232
232
 
233
233
  renderOption = (m, i) => {
@@ -663,29 +663,6 @@ export default class Setting extends Component {
663
663
  )
664
664
  }
665
665
 
666
- handlePortable = async () => {
667
- const {
668
- appPath,
669
- exePath
670
- } = this.props.store
671
- const from = osResolve(appPath, 'electerm', 'users')
672
- const tar = osResolve(exePath, 'electerm', 'users')
673
- const cmd = `xcopy /E /I /Q /Y "${from}" ${tar}`
674
- const x = await fs.runWinCmd(cmd)
675
- .catch(err => {
676
- this.props.store.onError(err)
677
- return false
678
- })
679
- if (x !== false) {
680
- message.success(
681
- `${e('dataTransferedTo')}: ${tar}`
682
- )
683
- setTimeout(
684
- this.props.store.restart, 5000
685
- )
686
- }
687
- }
688
-
689
666
  renderLoginPassAfter () {
690
667
  const {
691
668
  loginPass,
@@ -750,7 +727,8 @@ export default class Setting extends Component {
750
727
  hotkey,
751
728
  language,
752
729
  rendererType,
753
- theme
730
+ theme,
731
+ customCss
754
732
  } = this.props.config
755
733
  const {
756
734
  appPath,
@@ -848,6 +826,16 @@ export default class Setting extends Component {
848
826
  }
849
827
  </Select>
850
828
  </div>
829
+
830
+ <div className='pd2b'>
831
+ <span className='inline-title mg1r'>{e('customCss')}</span>
832
+ <Input.TextArea
833
+ onChange={this.handleCustomCss}
834
+ value={customCss}
835
+ rows={3}
836
+ />
837
+ </div>
838
+
851
839
  <div className='pd2b'>
852
840
  <span className='inline-title mg1r'>{e('language')}</span>
853
841
  <Select
@@ -1,10 +1,12 @@
1
1
  import Setting from './setting'
2
2
  import SettingCol from './col'
3
3
  import SyncSetting from '../setting-sync/setting-sync'
4
+ import Shortcuts from '../shortcuts/shortcuts'
4
5
  import List from './list'
5
6
  import {
6
7
  settingMap,
7
- settingSyncId
8
+ settingSyncId,
9
+ settingShortcutsId
8
10
  } from '../../common/constants'
9
11
 
10
12
  export default function TabSettings (props) {
@@ -19,6 +21,15 @@ export default function TabSettings (props) {
19
21
  listProps,
20
22
  store
21
23
  } = props
24
+ let elem = null
25
+ const sid = settingItem.id
26
+ if (sid === settingSyncId) {
27
+ elem = <SyncSetting store={store} />
28
+ } else if (sid === settingShortcutsId) {
29
+ elem = <Shortcuts store={store} />
30
+ } else {
31
+ elem = <Setting {...listProps} config={store.config} />
32
+ }
22
33
  return (
23
34
  <div
24
35
  className='setting-tabs-setting'
@@ -27,15 +38,7 @@ export default function TabSettings (props) {
27
38
  <List
28
39
  {...listProps}
29
40
  />
30
- {
31
- settingItem.id === settingSyncId
32
- ? (
33
- <SyncSetting
34
- store={store}
35
- />
36
- )
37
- : <Setting {...listProps} config={store.config} />
38
- }
41
+ {elem}
39
42
  </SettingCol>
40
43
  </div>
41
44
  )
@@ -21,7 +21,7 @@ export default function TransferTag (props) {
21
21
  const typeFromTitle = e(typeFrom)
22
22
  const typeToTitle = e(typeTo)
23
23
  const title = error
24
- ? `error: ${error}`
24
+ ? `[error: ${error}]`
25
25
  : ''
26
26
  return (
27
27
  <span className={'flex-child transfer-tag transfer-status-' + tagStatus} title={title}>
@@ -34,7 +34,7 @@ export default function TransferTag (props) {
34
34
  <span className='sftp-transfer-type'>
35
35
  {typeToTitle}
36
36
  </span>
37
- <span className='mg1l'>[{title}]</span>
37
+ <span className='mg1l'>{title}</span>
38
38
  </span>
39
39
  )
40
40
  }
@@ -222,6 +222,7 @@ export default function transportAction (props) {
222
222
  fromPathReal: transfer.fromPath,
223
223
  toPath: nTo,
224
224
  fromPath: p,
225
+ originalId: transfer.id,
225
226
  id: generate()
226
227
  }
227
228
  delete newTrans1.fromFile
@@ -0,0 +1,45 @@
1
+ export function getKeyCharacter (code = '') {
2
+ const mapping = {
3
+ Backquote: '`',
4
+ Minus: '-',
5
+ Equal: '=',
6
+ BracketLeft: '[',
7
+ BracketRight: ']',
8
+ Backslash: '\\',
9
+ NumpadDivide: '/',
10
+ NumpadMultiply: '*',
11
+ NumpadSubtract: '-',
12
+ Numpad7: 'N7',
13
+ Numpad8: 'N8',
14
+ Numpad9: 'N9',
15
+ NumpadAdd: '+',
16
+ Numpad4: 'N4',
17
+ Numpad5: 'N5',
18
+ Numpad6: 'N6',
19
+ Numpad1: 'N1',
20
+ Numpad2: 'N2',
21
+ Numpad3: 'N3',
22
+ NumpadEnter: 'Enter',
23
+ Numpad0: 'N0',
24
+ NumpadDecimal: '.',
25
+ IntlBackslash: '\\',
26
+ ArrowLeft: '←',
27
+ ArrowUp: '↑',
28
+ ArrowRight: '→',
29
+ ArrowDown: '↓',
30
+ Semicolon: ';',
31
+ Quote: '\'',
32
+ Comma: ',',
33
+ Period: '.',
34
+ Slash: '/',
35
+ mouseWheelUp: '▲',
36
+ mouseWheelDown: '▼'
37
+ }
38
+ if (code.startsWith('Key') && code.length === 4) {
39
+ return code[3].toLowerCase()
40
+ } else if (code.startsWith('Digit') && code.length === 5) {
41
+ return code[5]
42
+ } else {
43
+ return mapping[code] || code
44
+ }
45
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * session tabs component
3
+ * @param {array} props.tabs {id, title}
4
+ */
5
+
6
+ import React from 'react'
7
+ import { shortcutExtend } from './shortcut-handler.js'
8
+ import { throttle } from 'lodash-es'
9
+
10
+ class ShortcutControl extends React.PureComponent {
11
+ componentDidMount () {
12
+ window.addEventListener('keydown', this.handleKeyboardEvent.bind(this))
13
+ window.addEventListener('mousewheel', this.handleKeyboardEvent.bind(this))
14
+ }
15
+
16
+ prevTabShortcut = (e) => {
17
+ e.stopPropagation()
18
+ window.store.clickPrevTab()
19
+ }
20
+
21
+ nextTabShortcut = (e) => {
22
+ e.stopPropagation()
23
+ window.store.clickNextTab()
24
+ }
25
+
26
+ newBookmarkShortcut = (e) => {
27
+ e.stopPropagation()
28
+ window.store.onNewSsh()
29
+ }
30
+
31
+ togglefullscreenShortcut = throttle((e) => {
32
+ e.stopPropagation()
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)
43
+
44
+ zoominShortcut = (e) => {
45
+ e.stopPropagation()
46
+ window.store.zoom(0.25, true)
47
+ }
48
+
49
+ zoomoutShortcut = (e) => {
50
+ e.stopPropagation()
51
+ window.store.zoom(-0.25, true)
52
+ }
53
+
54
+ zoominTerminalShortcut = (event) => {
55
+ if (window.store.inActiveTerminal) {
56
+ window.store.zoomTerminal(event.wheelDeltaY)
57
+ } else {
58
+ const plus = event.wheelDeltaY > 0 ? 0.2 : -0.2
59
+ window.store.zoom(plus, true)
60
+ }
61
+ }
62
+
63
+ zoomoutTerminalShortcut = (event) => {
64
+ this.zoominTerminalShortcut(event)
65
+ }
66
+
67
+ render () {
68
+ return null
69
+ }
70
+ }
71
+
72
+ export default shortcutExtend(ShortcutControl)
@@ -0,0 +1,194 @@
1
+ import { PureComponent } from 'react'
2
+ import {
3
+ Button,
4
+ Input,
5
+ message
6
+ } from 'antd'
7
+ import {
8
+ EditFilled,
9
+ CheckOutlined,
10
+ CloseOutlined
11
+ } from '@ant-design/icons'
12
+ import { throttle } from 'lodash-es'
13
+ import { getKeyCharacter } from './get-key-char.js'
14
+
15
+ export default class ShortcutEdit extends PureComponent {
16
+ state = {
17
+ editMode: false,
18
+ shortcut: '',
19
+ data: null
20
+ }
21
+
22
+ addEventListener = () => {
23
+ const elem = document.querySelector('.ant-drawer')
24
+ elem?.addEventListener('click', this.handleClickOuter)
25
+ document.addEventListener('keydown', this.handleKeyDown)
26
+ document.addEventListener('mousewheel', this.handleKeyDown)
27
+ }
28
+
29
+ removeEventListener = () => {
30
+ const elem = document.querySelector('.ant-drawer')
31
+ elem?.removeEventListener('click', this.handleClickOuter)
32
+ document.removeEventListener('keydown', this.handleKeyDown)
33
+ document.removeEventListener('mousewheel', this.handleKeyDown)
34
+ }
35
+
36
+ isInsideElement = (event) => {
37
+ const { target } = event
38
+ const cls = this.getCls()
39
+ if (!target || !target.classList) {
40
+ return false
41
+ } else if (target.classList.contains(cls)) {
42
+ return true
43
+ } else {
44
+ const parent = target.parentElement
45
+ if (parent !== null) {
46
+ return this.isInsideElement({ target: parent })
47
+ } else {
48
+ return false
49
+ }
50
+ }
51
+ }
52
+
53
+ handleClickOuter = (e) => {
54
+ if (!this.isInsideElement(e)) {
55
+ this.handleCancel()
56
+ }
57
+ }
58
+
59
+ handleEditClick = () => {
60
+ this.setState({
61
+ editMode: true
62
+ }, this.addEventListener)
63
+ }
64
+
65
+ handleCancel = () => {
66
+ this.setState({
67
+ editMode: false
68
+ }, this.removeEventListener)
69
+ }
70
+
71
+ handleConfirm = () => {
72
+ const {
73
+ name
74
+ } = this.props.data
75
+ this.props.updateConfig(
76
+ name, this.state.shortcut
77
+ )
78
+ this.handleCancel()
79
+ }
80
+
81
+ getCls = () => {
82
+ const { index } = this.props.data
83
+ return 'shortcut-control-' + index
84
+ }
85
+
86
+ warnCtrolKey = throttle(() => {
87
+ message.info(
88
+ 'Must have one of Ctrl or Shift or Alt or Meta key',
89
+ undefined
90
+ )
91
+ }, 3000)
92
+
93
+ warnExist = throttle(() => {
94
+ message.info(
95
+ 'Shortcut already exists',
96
+ undefined
97
+ )
98
+ }, 3000)
99
+
100
+ handleKeyDown = (e) => {
101
+ const {
102
+ code,
103
+ ctrlKey,
104
+ shiftKey,
105
+ metaKey,
106
+ altKey,
107
+ wheelDeltaY
108
+ } = e
109
+ const codeName = e instanceof window.WheelEvent
110
+ ? (wheelDeltaY > 0 ? 'mouseWheelUp' : 'mouseWheelDown')
111
+ : code
112
+ const codeK = getKeyCharacter(codeName)
113
+ const noControlKey = !ctrlKey && !shiftKey && !metaKey && !altKey
114
+ if (noControlKey && codeK === 'Escape') {
115
+ return this.handleCancel()
116
+ } else if (noControlKey) {
117
+ return this.warnCtrolKey()
118
+ }
119
+ const r = (ctrlKey ? 'ctrl+' : '') +
120
+ (metaKey ? 'meta+' : '') +
121
+ (shiftKey ? 'shift+' : '') +
122
+ (altKey ? 'alt+' : '') +
123
+ codeK.toLowerCase()
124
+ if (this.props.keysTaken[r]) {
125
+ return this.warnExist()
126
+ }
127
+ this.setState({
128
+ shortcut: r
129
+ })
130
+ }
131
+
132
+ handleClickOutside = () => {
133
+ this.removeEventListener()
134
+ this.setState({
135
+ editMode: false
136
+ })
137
+ }
138
+
139
+ renderStatic () {
140
+ const {
141
+ shortcut
142
+ } = this.props.data
143
+ return (
144
+ <Button
145
+ className='edit-shortcut-button'
146
+ onClick={this.handleEditClick}
147
+ >
148
+ <span>{shortcut}</span>
149
+ <EditFilled
150
+ className='shortcut-edit-icon pointer mg1l'
151
+ />
152
+ </Button>
153
+ )
154
+ }
155
+
156
+ renderAfter () {
157
+ const {
158
+ shortcut
159
+ } = this.state
160
+ if (!shortcut) {
161
+ return null
162
+ }
163
+ return (
164
+ <div>
165
+ <CheckOutlined
166
+ onClick={this.handleConfirm}
167
+ className='pointer'
168
+ />
169
+ <CloseOutlined
170
+ onClick={this.handleCancel}
171
+ className='pointer mg1l'
172
+ />
173
+ </div>
174
+ )
175
+ }
176
+
177
+ render () {
178
+ const {
179
+ shortcut,
180
+ editMode
181
+ } = this.state
182
+ if (!editMode) {
183
+ return this.renderStatic()
184
+ }
185
+ return (
186
+ <div className={this.getCls()}>
187
+ <Input
188
+ addonAfter={this.renderAfter()}
189
+ value={shortcut}
190
+ />
191
+ </div>
192
+ )
193
+ }
194
+ }
@@ -0,0 +1,75 @@
1
+ import { getKeyCharacter } from './get-key-char.js'
2
+ import shortcutsDefaultsGen from './shortcuts-defaults.js'
3
+ import {
4
+ isMacJs
5
+ } from '../../common/constants'
6
+
7
+ function buildConfig (config) {
8
+ const defs = shortcutsDefaultsGen()
9
+ const { shortcuts = {} } = config
10
+ return defs.reduce((p, c) => {
11
+ const propName = isMacJs ? 'shortcutMac' : 'shortcut'
12
+ const name = c.name + '_' + propName
13
+ const [type, func] = c.name.split('_')
14
+ return {
15
+ ...p,
16
+ [name]: {
17
+ shortcut: c.readonly ? c[propName] : (shortcuts[name] || c[propName]),
18
+ type,
19
+ func
20
+ }
21
+ }
22
+ }, {})
23
+ }
24
+
25
+ export function shortcutExtend (Cls) {
26
+ Cls.prototype.handleKeyboardEvent = function (event) {
27
+ const {
28
+ code,
29
+ ctrlKey,
30
+ shiftKey,
31
+ metaKey,
32
+ altKey,
33
+ wheelDeltaY
34
+ } = event
35
+ const codeName = event instanceof window.WheelEvent
36
+ ? (wheelDeltaY > 0 ? 'mouseWheelUp' : 'mouseWheelDown')
37
+ : code
38
+ const codeK = getKeyCharacter(codeName)
39
+ const noControlKey = !ctrlKey && !shiftKey && !metaKey && !altKey
40
+ if (noControlKey) {
41
+ return
42
+ }
43
+ const r = (ctrlKey ? 'ctrl+' : '') +
44
+ (metaKey ? 'meta+' : '') +
45
+ (shiftKey ? 'shift+' : '') +
46
+ (altKey ? 'alt+' : '') +
47
+ codeK.toLowerCase()
48
+ const shortcutsConfig = buildConfig(this.props.config)
49
+ const keys = Object.keys(shortcutsConfig)
50
+ const len = keys.length
51
+ for (let i = 0; i < len; i++) {
52
+ const k = keys[i]
53
+ const conf = shortcutsConfig[k]
54
+ const funcName = conf.func + 'Shortcut'
55
+ if (conf.shortcut.split(',').includes(r)) {
56
+ if (this[funcName]) {
57
+ this[funcName](event)
58
+ } else {
59
+ return false
60
+ }
61
+ }
62
+ }
63
+ }
64
+ return Cls
65
+ }
66
+
67
+ export function shortcutDescExtend (Cls) {
68
+ Cls.prototype.getShortcut = function (name) {
69
+ const shortcutsConfig = buildConfig(this.props.config)
70
+ const propName = isMacJs ? 'shortcutMac' : 'shortcut'
71
+ const n = `${name}_${propName}`
72
+ return shortcutsConfig[n].shortcut
73
+ }
74
+ return Cls
75
+ }
File without changes
@@ -0,0 +1,92 @@
1
+ export default () => {
2
+ return [
3
+ {
4
+ name: 'app_closeCurrentTab',
5
+ shortcut: 'alt+w',
6
+ shortcutMac: 'alt+w'
7
+ },
8
+ {
9
+ name: 'app_newBookmark',
10
+ shortcut: 'ctrl+n',
11
+ shortcutMac: 'meta+n'
12
+ },
13
+ {
14
+ name: 'app_togglefullscreen',
15
+ shortcut: 'alt+f',
16
+ shortcutMac: 'alt+f'
17
+ },
18
+ {
19
+ name: 'app_zoomin',
20
+ shortcut: 'ctrl+=',
21
+ shortcutMac: 'meta+='
22
+ },
23
+ {
24
+ name: 'app_zoomout',
25
+ shortcut: 'ctrl+-',
26
+ shortcutMac: 'meta+-'
27
+ },
28
+ {
29
+ name: 'app_prevTab',
30
+ shortcut: 'ctrl+shift+tab',
31
+ shortcutMac: 'ctrl+shift+tab'
32
+ },
33
+ {
34
+ name: 'app_nextTab',
35
+ shortcut: 'ctrl+tab',
36
+ shortcutMac: 'ctrl+tab'
37
+ },
38
+ {
39
+ name: 'terminal_split',
40
+ shortcut: 'ctrl+/',
41
+ shortcutMac: 'meta+/'
42
+ },
43
+ {
44
+ name: 'terminal_clear',
45
+ shortcut: 'ctrl+l,ctrl+shift+l',
46
+ shortcutMac: 'meta+l'
47
+ },
48
+ {
49
+ name: 'terminal_selectAll',
50
+ shortcut: 'ctrl+a,ctrl+shift+a',
51
+ shortcutMac: 'meta+a',
52
+ readonly: true
53
+ },
54
+ {
55
+ name: 'terminal_copy',
56
+ shortcut: 'ctrl+c,ctrl+shift+c',
57
+ shortcutMac: 'meta+c',
58
+ readonly: true
59
+ },
60
+ {
61
+ name: 'terminal_paste',
62
+ shortcut: 'ctrl+v,ctrl+shift+v',
63
+ shortcutMac: 'meta+v',
64
+ readonly: true
65
+ },
66
+ {
67
+ name: 'terminal_search',
68
+ shortcut: 'ctrl+f',
69
+ shortcutMac: 'meta+f'
70
+ },
71
+ {
72
+ name: 'terminal_pasteSelected',
73
+ shortcut: 'alt+insert',
74
+ shortcutMac: 'alt+insert'
75
+ },
76
+ {
77
+ name: 'terminal_showNormalBuffer',
78
+ shortcut: 'ctrl+ArrowUp',
79
+ shortcutMac: 'meta+↑'
80
+ },
81
+ {
82
+ name: 'terminal_zoominTerminal',
83
+ shortcut: 'ctrl+▲',
84
+ shortcutMac: 'meta+▲'
85
+ },
86
+ {
87
+ name: 'terminal_zoomoutTerminal',
88
+ shortcut: 'ctrl+▼',
89
+ shortcutMac: 'meta+▼'
90
+ }
91
+ ]
92
+ }