@electerm/electerm-react 5.3.26 → 5.5.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 (134) hide show
  1. package/README.md +246 -12
  2. package/client/common/choose-save-folder.js +21 -2
  3. package/client/common/constants.js +20 -6
  4. package/client/common/create-title.jsx +6 -0
  5. package/client/common/default-setting.js +14 -0
  6. package/client/common/init-setting-item.js +5 -0
  7. package/client/common/platform.js +24 -0
  8. package/client/components/ai/agent-tool-call-card.jsx +10 -2
  9. package/client/components/ai/agent.js +4 -1
  10. package/client/components/ai/ai-attachments.js +87 -0
  11. package/client/components/ai/ai-chat-empty.jsx +43 -0
  12. package/client/components/ai/ai-chat-entry.jsx +13 -4
  13. package/client/components/ai/ai-chat-history-item.jsx +28 -4
  14. package/client/components/ai/ai-chat-history.jsx +2 -1
  15. package/client/components/ai/ai-chat.jsx +104 -13
  16. package/client/components/ai/ai-config-props.js +4 -130
  17. package/client/components/ai/ai-config.jsx +135 -15
  18. package/client/components/ai/ai-presets.js +173 -0
  19. package/client/components/ai/ai.styl +83 -0
  20. package/client/components/bg/css-overwrite.jsx +11 -0
  21. package/client/components/bookmark-form/bookmark-schema.js +4 -0
  22. package/client/components/bookmark-form/common/fields.jsx +3 -0
  23. package/client/components/bookmark-form/config/common-fields.js +12 -0
  24. package/client/components/bookmark-form/config/local.js +3 -2
  25. package/client/components/bookmark-form/config/serial.js +3 -2
  26. package/client/components/bookmark-form/config/ssh.js +2 -1
  27. package/client/components/bookmark-form/config/telnet.js +3 -2
  28. package/client/components/common/item-filter.jsx +76 -0
  29. package/client/components/common/item-filter.styl +54 -0
  30. package/client/components/common/lazy-boundary.jsx +34 -0
  31. package/client/components/common/opacity.jsx +3 -1
  32. package/client/components/footer/footer-entry.jsx +35 -5
  33. package/client/components/footer/footer.styl +8 -1
  34. package/client/components/layout/layout.jsx +23 -31
  35. package/client/components/layout/pixed.js +3 -0
  36. package/client/components/main/main.jsx +55 -28
  37. package/client/components/main/term-fullscreen.styl +9 -3
  38. package/client/components/quick-commands/qm.styl +3 -2
  39. package/client/components/rdp/file-transfer.js +4 -4
  40. package/client/components/remote-monitor/monitor-details.jsx +458 -0
  41. package/client/components/remote-monitor/monitor-details.styl +87 -0
  42. package/client/components/remote-monitor/monitor-model.js +764 -0
  43. package/client/components/remote-monitor/remote-monitor-bar-entry.jsx +18 -0
  44. package/client/components/remote-monitor/remote-monitor-bar.jsx +344 -0
  45. package/client/components/remote-monitor/remote-monitor-bar.styl +106 -0
  46. package/client/components/remote-monitor/session-monitor.js +361 -0
  47. package/client/components/remote-monitor/use-monitor-details.js +10 -0
  48. package/client/components/remote-monitor/visibility.js +18 -0
  49. package/client/components/session/session-control.jsx +29 -2
  50. package/client/components/session/session.jsx +40 -18
  51. package/client/components/session/sessions.jsx +5 -2
  52. package/client/components/setting-panel/setting-common.jsx +25 -56
  53. package/client/components/setting-panel/setting-modal.jsx +57 -42
  54. package/client/components/setting-panel/setting-terminal.jsx +7 -2
  55. package/client/components/setting-panel/setting.styl +1 -1
  56. package/client/components/setting-panel/tab-triggers.jsx +71 -0
  57. package/client/components/sftp/file-compare-modal.jsx +6 -3
  58. package/client/components/sftp/file-item.jsx +5 -3
  59. package/client/components/sftp/owner-list.js +11 -2
  60. package/client/components/side-panel-r/side-panel-r.jsx +2 -2
  61. package/client/components/sys-menu/menu-btn.jsx +30 -1
  62. package/client/components/tabs/index.jsx +2 -2
  63. package/client/components/tabs/tab-title.jsx +3 -2
  64. package/client/components/tabs/tab.jsx +5 -1
  65. package/client/components/terminal/attach-addon-custom.js +295 -49
  66. package/client/components/terminal/automation/index.js +64 -0
  67. package/client/components/terminal/automation/keys.js +49 -0
  68. package/client/components/terminal/automation/stream-matcher.js +82 -0
  69. package/client/components/terminal/automation/strip-ansi.js +19 -0
  70. package/client/components/terminal/automation/trigger-engine.js +233 -0
  71. package/client/components/terminal/automation/trigger-presets.js +110 -0
  72. package/client/components/terminal/command-tracker-addon.js +31 -1
  73. package/client/components/terminal/mixins/index.js +27 -0
  74. package/client/components/terminal/mixins/term-attach.js +123 -0
  75. package/client/components/terminal/mixins/term-config.jsx +84 -0
  76. package/client/components/terminal/mixins/term-content.js +88 -0
  77. package/client/components/terminal/mixins/term-context-menu.jsx +291 -0
  78. package/client/components/terminal/mixins/term-file-drop.js +149 -0
  79. package/client/components/terminal/mixins/term-init.js +122 -0
  80. package/client/components/terminal/mixins/term-log.jsx +96 -0
  81. package/client/components/terminal/mixins/term-resize.js +87 -0
  82. package/client/components/terminal/mixins/term-socket.js +433 -0
  83. package/client/components/terminal/mixins/term-suggestions.js +224 -0
  84. package/client/components/terminal/mixins/term-theme.js +190 -0
  85. package/client/components/terminal/mixins/term-touch.js +136 -0
  86. package/client/components/terminal/shell-detect.js +37 -0
  87. package/client/components/terminal/shell.js +0 -28
  88. package/client/components/terminal/shortcut-bar-entry.jsx +6 -3
  89. package/client/components/terminal/shortcut-bar.jsx +4 -6
  90. package/client/components/terminal/shortcut-bar.styl +4 -0
  91. package/client/components/terminal/ssh-reload-state.js +56 -0
  92. package/client/components/terminal/startup-queue.js +379 -0
  93. package/client/components/terminal/terminal-interactive.jsx +32 -0
  94. package/client/components/terminal/terminal.jsx +102 -2007
  95. package/client/components/terminal/transfer-client-base.js +5 -29
  96. package/client/components/terminal/trzsz-client.js +1 -3
  97. package/client/components/terminal/xmodem-client.js +1 -3
  98. package/client/components/terminal/xterm-loader.js +7 -0
  99. package/client/components/terminal/zmodem-client.js +1 -3
  100. package/client/components/terminal-info/base.jsx +18 -70
  101. package/client/components/terminal-info/terminal-info-entry.jsx +13 -5
  102. package/client/components/terminal-info/terminal-info.jsx +50 -59
  103. package/client/components/terminal-info/terminal-info.styl +6 -49
  104. package/client/components/text-editor/open-text-editor.js +30 -0
  105. package/client/components/text-editor/text-editor-entry.jsx +17 -6
  106. package/client/components/text-editor/text-editor.jsx +14 -0
  107. package/client/components/triggers/trigger-editor.jsx +246 -0
  108. package/client/components/triggers/trigger-form.jsx +200 -0
  109. package/client/components/triggers/trigger-session-modal.jsx +199 -0
  110. package/client/components/triggers/trigger-setting-list.jsx +89 -0
  111. package/client/components/triggers/trigger-transport.jsx +60 -0
  112. package/client/components/triggers/triggers-field.jsx +19 -0
  113. package/client/components/widgets/widgets-list.jsx +11 -1
  114. package/client/css/mobile.styl +9 -1
  115. package/client/store/common.js +12 -1
  116. package/client/store/init-state.js +7 -0
  117. package/client/store/mcp-handler.js +158 -30
  118. package/client/store/setting.js +9 -0
  119. package/client/store/store.js +2 -0
  120. package/client/store/sync.js +2 -0
  121. package/client/store/tab.js +41 -3
  122. package/client/store/trigger.js +118 -0
  123. package/client/store/watch.js +13 -5
  124. package/client/views/index.pug +3 -0
  125. package/package.json +1 -1
  126. package/client/components/terminal-info/activity.jsx +0 -57
  127. package/client/components/terminal-info/data-cols-parser.jsx +0 -54
  128. package/client/components/terminal-info/disk.jsx +0 -45
  129. package/client/components/terminal-info/format-disks.js +0 -45
  130. package/client/components/terminal-info/network.jsx +0 -121
  131. package/client/components/terminal-info/resource.jsx +0 -99
  132. package/client/components/terminal-info/run-cmd.jsx +0 -263
  133. package/client/components/terminal-info/sys-info.jsx +0 -56
  134. package/client/components/terminal-info/up.jsx +0 -17
@@ -11,7 +11,6 @@ import SwitchLabel from '../common/switch'
11
11
  import {
12
12
  Select,
13
13
  Button,
14
- Table,
15
14
  Space,
16
15
  Tag
17
16
  } from 'antd'
@@ -22,7 +21,8 @@ import InputNumberConfirm from '../common/input-number-confirm'
22
21
  import TextareaConfirm from '../common/textarea-confirm'
23
22
  import {
24
23
  settingMap,
25
- proxyHelpLink
24
+ proxyHelpLink,
25
+ webAppHiddenSettings
26
26
  } from '../../common/constants'
27
27
  import defaultSettings from '../../common/default-setting'
28
28
  import Link from '../common/external-link'
@@ -320,56 +320,13 @@ export default class SettingCommon extends Component {
320
320
  const {
321
321
  enableGlobalProxy
322
322
  } = this.props.config
323
- const helps = `http# http://proxy-server-over-tcp.com:3128
324
- https#https://proxy-server-over-tls.com:3129
325
- socks(v5)#socks://username:password@some-socks-proxy.com:9050 (username & password are optional)
326
- socks5#socks5://username:password@some-socks-proxy.com:9050 (username & password are optional)
327
- socks5h#socks5h://username:password@some-socks-proxy.com:9050 (username & password are optional)
328
- socks4#socks4://some-socks-proxy.com:9050
329
- socks4a#socks4a://some-socks-proxy.com:9050`
330
- .split('\n')
331
- .filter(d => d.trim())
332
- .map(d => {
333
- const [protocol, example] = d.split('#')
334
- return {
335
- protocol, example
336
- }
337
- })
338
- const cols = Object.keys(helps[0]).map(k => {
339
- return {
340
- title: k,
341
- dataIndex: k,
342
- key: k,
343
- render: (k) => k || ''
344
- }
345
- })
346
- const table = (
347
- <div>
348
- <Table
349
- columns={cols}
350
- dataSource={helps}
351
- bordered
352
- pagination={false}
353
- size='small'
354
- rowKey='protocol'
355
- />
356
- <div>
357
- <Link to={proxyHelpLink}>{proxyHelpLink}</Link>
358
- </div>
359
- </div>
360
- )
361
- const style = {
362
- height: '414px',
363
- width: '500px'
364
- }
365
323
  return (
366
324
  <div className='pd1b'>
367
325
  <div className='pd1b'>
368
326
  <span className='pd1r'>
369
327
  {e('global')} {e('proxy')}
370
328
  <HelpIcon
371
- title={table}
372
- style={{ body: { style } }}
329
+ link={proxyHelpLink}
373
330
  />
374
331
  </span>
375
332
  <SwitchLabel
@@ -456,6 +413,7 @@ export default class SettingCommon extends Component {
456
413
  const {
457
414
  langs = []
458
415
  } = window.et
416
+ const isWebApp = !!window.et.isWebApp
459
417
  const terminalThemes = props.store.getSidebarList(settingMap.terminalThemes)
460
418
  const pops = {
461
419
  onStartSessions: props.config.onStartSessions,
@@ -475,9 +433,15 @@ export default class SettingCommon extends Component {
475
433
  config={props.config}
476
434
  store={props.store}
477
435
  />
478
- <HotkeySetting
479
- {...hotkeyProps}
480
- />
436
+ {
437
+ isWebApp
438
+ ? null
439
+ : (
440
+ <HotkeySetting
441
+ {...hotkeyProps}
442
+ />
443
+ )
444
+ }
481
445
  <div className='pd1b'>{e('onStartBookmarks')}</div>
482
446
  <div className='pd2b'>
483
447
  <StartSession
@@ -502,12 +466,14 @@ export default class SettingCommon extends Component {
502
466
  }, e('keepaliveIntervalDesc'))
503
467
  }
504
468
  {
505
- this.renderNumber('opacity', {
506
- step: 0.05,
507
- min: 0,
508
- max: 1,
509
- cls: 'opacity'
510
- }, e('opacity'))
469
+ isWebApp
470
+ ? null
471
+ : this.renderNumber('opacity', {
472
+ step: 0.05,
473
+ min: 0,
474
+ max: 1,
475
+ cls: 'opacity'
476
+ }, e('opacity'))
511
477
  }
512
478
 
513
479
  <div className='pd2b'>
@@ -608,9 +574,12 @@ export default class SettingCommon extends Component {
608
574
  'allowMultiInstance',
609
575
  'disableDeveloperTool',
610
576
  'switchTabOnHover',
577
+ 'disableTabIndex',
611
578
  'disableShortcutBar',
612
579
  'debug'
613
- ].map(this.renderToggle)
580
+ ]
581
+ .filter(name => !isWebApp || !webAppHiddenSettings.includes(name))
582
+ .map(this.renderToggle)
614
583
  }
615
584
  {
616
585
  window.et.isWebApp ? null : <DeepLinkControl />
@@ -7,6 +7,7 @@ import { pick } from 'lodash-es'
7
7
  import { Tabs, Spin } from 'antd'
8
8
  import { lazy, Suspense } from 'react'
9
9
  import SettingModal from './setting-wrap'
10
+ import LazyBoundary from '../common/lazy-boundary'
10
11
  import {
11
12
  settingMap,
12
13
  modals,
@@ -18,6 +19,7 @@ const TabSettings = lazy(() => import('./tab-settings'))
18
19
  const TabThemes = lazy(() => import('./tab-themes'))
19
20
  const TabProfiles = lazy(() => import('./tab-profiles'))
20
21
  const TabWidgets = lazy(() => import('./tab-widgets'))
22
+ const TabTriggers = lazy(() => import('./tab-triggers'))
21
23
 
22
24
  const Loading = () => <div style={{ padding: 20, textAlign: 'center' }}><Spin /></div>
23
25
 
@@ -107,6 +109,11 @@ export default auto(function SettingModalWrap (props) {
107
109
  label: e(settingMap.quickCommands),
108
110
  children: null
109
111
  },
112
+ {
113
+ key: settingMap.triggers,
114
+ label: <span>{e('triggers')} <sup>Beta</sup></span>,
115
+ children: null
116
+ },
110
117
  {
111
118
  key: settingMap.profiles,
112
119
  label: e(settingMap.profiles),
@@ -132,48 +139,56 @@ export default auto(function SettingModalWrap (props) {
132
139
  <Tabs
133
140
  {...tabsProps}
134
141
  />
135
- <Suspense fallback={<Loading />}>
136
- <TabQuickCommands
137
- listProps={props0}
138
- settingItem={settingItem}
139
- formProps={formProps}
140
- store={store}
141
- settingTab={settingTab}
142
- />
143
- <TabBookmarks
144
- treeProps={treeProps}
145
- settingItem={settingItem}
146
- formProps={formProps}
147
- settingTab={settingTab}
148
- />
149
- <TabSettings
150
- listProps={props0}
151
- settingItem={settingItem}
152
- settingTab={settingTab}
153
- store={store}
154
- />
155
- <TabThemes
156
- listProps={props0}
157
- settingItem={settingItem}
158
- formProps={formProps}
159
- store={store}
160
- settingTab={settingTab}
161
- />
162
- <TabProfiles
163
- listProps={props0}
164
- settingItem={settingItem}
165
- formProps={formProps}
166
- store={store}
167
- settingTab={settingTab}
168
- />
169
- <TabWidgets
170
- listProps={props0}
171
- settingItem={settingItem}
172
- formProps={formProps}
173
- store={store}
174
- settingTab={settingTab}
175
- />
176
- </Suspense>
142
+ <LazyBoundary>
143
+ <Suspense fallback={<Loading />}>
144
+ <TabQuickCommands
145
+ listProps={props0}
146
+ settingItem={settingItem}
147
+ formProps={formProps}
148
+ store={store}
149
+ settingTab={settingTab}
150
+ />
151
+ <TabTriggers
152
+ listProps={props0}
153
+ settingItem={settingItem}
154
+ store={store}
155
+ settingTab={settingTab}
156
+ />
157
+ <TabBookmarks
158
+ treeProps={treeProps}
159
+ settingItem={settingItem}
160
+ formProps={formProps}
161
+ settingTab={settingTab}
162
+ />
163
+ <TabSettings
164
+ listProps={props0}
165
+ settingItem={settingItem}
166
+ settingTab={settingTab}
167
+ store={store}
168
+ />
169
+ <TabThemes
170
+ listProps={props0}
171
+ settingItem={settingItem}
172
+ formProps={formProps}
173
+ store={store}
174
+ settingTab={settingTab}
175
+ />
176
+ <TabProfiles
177
+ listProps={props0}
178
+ settingItem={settingItem}
179
+ formProps={formProps}
180
+ store={store}
181
+ settingTab={settingTab}
182
+ />
183
+ <TabWidgets
184
+ listProps={props0}
185
+ settingItem={settingItem}
186
+ formProps={formProps}
187
+ store={store}
188
+ settingTab={settingTab}
189
+ />
190
+ </Suspense>
191
+ </LazyBoundary>
177
192
  </>
178
193
  )
179
194
  }
@@ -509,7 +509,7 @@ export default class SettingTerminal extends Component {
509
509
  {
510
510
  this.renderNumber('fontSize', {
511
511
  step: 1,
512
- min: 9
512
+ min: 5
513
513
  }, `${e('default')} ${e('fontSize')}`, 400)
514
514
  }
515
515
  <div className='pd2b'>
@@ -575,9 +575,14 @@ export default class SettingTerminal extends Component {
575
575
  'sftpPathFollowSsh',
576
576
  'sshSftpSplitView',
577
577
  'showCmdSuggestions',
578
- 'autoReconnectTerminal'
578
+ 'autoReconnectTerminal',
579
+ 'restoreTerminalSessionOnReload'
579
580
  ].map(d => this.renderToggle(d))
580
581
  }
582
+ {
583
+ // label differs from the config key
584
+ this.renderToggle('remoteMonitorBarEnabled', 'pd2b', e('monitorBar'))
585
+ }
581
586
  <div className='pd1b'>{e('dragDropBehavior')}</div>
582
587
  <Select
583
588
  onChange={this.handleChangeDragDropBehavior}
@@ -46,4 +46,4 @@
46
46
  line-height 1.5
47
47
  .setting-passwords-actions-cell
48
48
  flex-shrink 0
49
- margin-left auto
49
+ margin-left auto
@@ -0,0 +1,71 @@
1
+ import { Button, Form } from 'antd'
2
+ import SettingCol from './col'
3
+ import TriggerSettingList from '../triggers/trigger-setting-list'
4
+ import TriggerForm, { buildTriggerFromFormValues } from '../triggers/trigger-form'
5
+ import { validateTriggers } from '../terminal/automation/trigger-engine'
6
+ import message from '../common/message'
7
+ import { settingMap } from '../../common/constants'
8
+
9
+ const e = window.translate
10
+
11
+ export default function TabTriggers (props) {
12
+ const [form] = Form.useForm()
13
+ const {
14
+ settingTab,
15
+ store,
16
+ settingItem,
17
+ listProps
18
+ } = props
19
+ if (settingTab !== settingMap.triggers) {
20
+ return null
21
+ }
22
+ const handleSave = async () => {
23
+ try {
24
+ const v = await form.validateFields()
25
+ const next = buildTriggerFromFormValues(settingItem, v)
26
+ const errors = validateTriggers([next])
27
+ if (errors.length) {
28
+ message.error(errors[0])
29
+ return
30
+ }
31
+ if (settingItem.id) {
32
+ store.editTrigger(settingItem.id, next)
33
+ } else {
34
+ store.addTrigger(next)
35
+ store.setSettingItem({
36
+ id: '',
37
+ name: settingMap.triggers
38
+ })
39
+ }
40
+ message.success(e('saved'))
41
+ } catch (err) {
42
+ // validation failed, keep form as is
43
+ }
44
+ }
45
+
46
+ return (
47
+ <div
48
+ className='setting-tabs-triggers'
49
+ >
50
+ <SettingCol>
51
+ <TriggerSettingList
52
+ {...listProps}
53
+ />
54
+ <div key={settingItem.id}>
55
+ <TriggerForm
56
+ form={form}
57
+ initial={settingItem}
58
+ />
59
+ <div className='pd2l'>
60
+ <Button
61
+ type='primary'
62
+ onClick={handleSave}
63
+ >
64
+ {e('save')}
65
+ </Button>
66
+ </div>
67
+ </div>
68
+ </SettingCol>
69
+ </div>
70
+ )
71
+ }
@@ -12,6 +12,7 @@ import { mode2permission, permission2mode } from '../../common/mode2permission'
12
12
  import FileIcon from './file-icon'
13
13
  import { filesize } from 'filesize'
14
14
  import { refsStatic } from '../common/ref'
15
+ import LazyBoundary from '../common/lazy-boundary'
15
16
  import './file-compare-modal.styl'
16
17
 
17
18
  const e = window.translate
@@ -169,9 +170,11 @@ export default class FileCompareModal extends React.PureComponent {
169
170
  key: 'code',
170
171
  label: e('code'),
171
172
  children: (
172
- <Suspense fallback={<Loading />}>
173
- <CodeCompare file1={file1} file2={file2} tab={tab} />
174
- </Suspense>
173
+ <LazyBoundary>
174
+ <Suspense fallback={<Loading />}>
175
+ <CodeCompare file1={file1} file2={file2} tab={tab} />
176
+ </Suspense>
177
+ </LazyBoundary>
175
178
  )
176
179
  }
177
180
  ]
@@ -36,6 +36,7 @@ import { filesize } from 'filesize'
36
36
  import { createTransferProps } from './transfer-common'
37
37
  import generate from '../../common/uid'
38
38
  import sanitizeFilename from '../../common/sanitize-filename'
39
+ import { openTextEditor } from '../text-editor/open-text-editor'
39
40
  import { refsStatic, refs, filesRef } from '../common/ref'
40
41
  import iconsMap from '../sys-menu/icons-map'
41
42
  import message from '../common/message'
@@ -702,7 +703,7 @@ export default class FileSection extends React.Component {
702
703
  }
703
704
 
704
705
  onFileChange = (e, text) => {
705
- this.editor.editWithSystemEditorDone({
706
+ this.editor?.editWithSystemEditorDone({
706
707
  id: this.id,
707
708
  text
708
709
  })
@@ -773,7 +774,8 @@ export default class FileSection extends React.Component {
773
774
 
774
775
  editFile = () => {
775
776
  refs.add(this.id, this)
776
- this.editor?.openEditor({
777
+ // editor is lazy loaded, this will mount it on demand
778
+ openTextEditor({
777
779
  id: this.id,
778
780
  file: this.state.file
779
781
  })
@@ -1261,7 +1263,7 @@ export default class FileSection extends React.Component {
1261
1263
  if (isDirectory && id === 'size') {
1262
1264
  value = null
1263
1265
  } else if (!isDirectory && id === 'size') {
1264
- value = filesize(value)
1266
+ value = filesize(Number(value) || 0)
1265
1267
  } else if (id === 'owner') {
1266
1268
  const { type } = this.props
1267
1269
  value = this.props[`${type}UidTree`]['' + value] || value
@@ -26,6 +26,15 @@ function parseNames (str) {
26
26
  const linuxListUser = 'cat /etc/passwd'
27
27
  const linuxListGroup = 'cat /etc/group'
28
28
 
29
+ // The local shell (node-bash) only exists on desktop builds; web/mobile builds
30
+ // (electerm-web in a browser, electerm-ios/electerm-android) ship without it, so
31
+ // `fs.run` rejects. These owner/group lists are a best-effort nicety for the
32
+ // local file panel, and every rejection would surface as a spurious error
33
+ // notification on those platforms — skip the probe entirely when we know there
34
+ // is no local shell. `hasNodePty === false` is the explicit signal reported by
35
+ // the server (see checkNodePty); an absent flag keeps desktop behaviour intact.
36
+ const noLocalShell = isWin || window.et.hasNodePty === false
37
+
29
38
  export async function remoteListUsers (pid) {
30
39
  const users = await runCmd(pid, linuxListUser)
31
40
  .catch(console.error)
@@ -45,7 +54,7 @@ export async function remoteListGroups (pid) {
45
54
  }
46
55
 
47
56
  export async function localListUsers () {
48
- if (isWin) {
57
+ if (noLocalShell) {
49
58
  return {}
50
59
  } else if (isMac) {
51
60
  const g = await window.fs.run('dscl . -list /Users UniqueID')
@@ -72,7 +81,7 @@ export async function localListUsers () {
72
81
  }
73
82
 
74
83
  export async function localListGroups () {
75
- if (isWin) {
84
+ if (noLocalShell) {
76
85
  return {}
77
86
  } else if (isMac) {
78
87
  const g = await window.fs.run('dscl . list /Groups PrimaryGroupID')
@@ -4,7 +4,7 @@ import './right-side-panel.styl'
4
4
  import {
5
5
  CloseCircleOutlined,
6
6
  PushpinOutlined,
7
- InfoCircleOutlined
7
+ BarChartOutlined
8
8
  } from '@ant-design/icons'
9
9
  import {
10
10
  Typography,
@@ -29,7 +29,7 @@ export default memo(function RightSidePanel (
29
29
  }
30
30
  const tag = rightPanelTab === 'ai'
31
31
  ? <Tag className='mg1r'>AI</Tag>
32
- : <InfoCircleOutlined className='mg1r' />
32
+ : <BarChartOutlined className='mg1r' />
33
33
 
34
34
  function onDragEnd (nw) {
35
35
  window.store.setRightSidePanelWidth(nw)
@@ -65,6 +65,7 @@ class MenuBtn extends PureComponent {
65
65
  renderContext = () => {
66
66
  const items = [
67
67
  {
68
+ key: 'onNewSsh',
68
69
  func: 'onNewSsh',
69
70
  icon: 'CodeFilled',
70
71
  text: e('newBookmark'),
@@ -73,6 +74,7 @@ class MenuBtn extends PureComponent {
73
74
  ]
74
75
  if (window.store.hasNodePty) {
75
76
  items.push({
77
+ key: 'addTab',
76
78
  func: 'addTab',
77
79
  icon: 'RightSquareFilled',
78
80
  text: e('newTab'),
@@ -83,6 +85,7 @@ class MenuBtn extends PureComponent {
83
85
  // type: 'hr'
84
86
  // },
85
87
  items.push({
88
+ key: 'bookmarks',
86
89
  noCloseMenu: true,
87
90
  icon: 'BookOutlined',
88
91
  text: e('bookmarks'),
@@ -90,18 +93,21 @@ class MenuBtn extends PureComponent {
90
93
  })
91
94
  items.push(
92
95
  {
96
+ key: 'history',
93
97
  noCloseMenu: true,
94
98
  icon: 'ClockCircleOutlined',
95
99
  text: e('history'),
96
100
  submenu: 'History'
97
101
  },
98
102
  {
103
+ key: 'sessions',
99
104
  noCloseMenu: true,
100
105
  icon: 'BarsOutlined',
101
106
  text: e('sessions'),
102
107
  submenu: 'Tabs'
103
108
  },
104
109
  {
110
+ key: 'layout',
105
111
  icon: 'AppstoreOutlined',
106
112
  text: e('layout'),
107
113
  submenu: 'Layout'
@@ -110,16 +116,19 @@ class MenuBtn extends PureComponent {
110
116
  // type: 'hr'
111
117
  // },
112
118
  {
119
+ key: 'openAbout',
113
120
  func: 'openAbout',
114
121
  icon: 'InfoCircleOutlined',
115
122
  text: e('about')
116
123
  },
117
124
  {
125
+ key: 'openSetting',
118
126
  func: 'openSetting',
119
127
  icon: 'SettingOutlined',
120
128
  text: e('settings')
121
129
  },
122
130
  {
131
+ key: 'openDevTools',
123
132
  func: 'openDevTools',
124
133
  icon: 'LeftSquareFilled',
125
134
  text: e('toggledevtools')
@@ -128,19 +137,23 @@ class MenuBtn extends PureComponent {
128
137
  // type: 'hr'
129
138
  // },
130
139
  {
140
+ key: 'zoom',
131
141
  module: 'Zoom'
132
142
  },
133
143
  {
144
+ key: 'minimize',
134
145
  func: 'minimize',
135
146
  icon: 'SwitcherFilled',
136
147
  text: e('minimize')
137
148
  },
138
149
  {
150
+ key: 'maximize',
139
151
  func: 'maximize',
140
152
  icon: 'LayoutFilled',
141
153
  text: e('maximize')
142
154
  },
143
155
  {
156
+ key: 'reload',
144
157
  func: 'reload',
145
158
  icon: 'ReloadOutlined',
146
159
  text: e('reload')
@@ -149,6 +162,7 @@ class MenuBtn extends PureComponent {
149
162
  // type: 'hr'
150
163
  // },
151
164
  {
165
+ key: 'onCheckUpdate',
152
166
  func: 'onCheckUpdate',
153
167
  icon: 'UpCircleOutlined',
154
168
  text: e('checkForUpdate')
@@ -157,17 +171,32 @@ class MenuBtn extends PureComponent {
157
171
  // type: 'hr'
158
172
  // },
159
173
  {
174
+ key: 'restart',
160
175
  func: 'restart',
161
176
  icon: 'RedoOutlined',
162
177
  text: e('restart')
163
178
  },
164
179
  {
180
+ key: 'close',
165
181
  func: 'close',
166
182
  icon: 'CloseOutlined',
167
183
  text: e('close')
168
184
  }
169
185
  )
170
- return items
186
+ return this.filterMenus(items)
187
+ }
188
+
189
+ // Only show menus listed in window.et.sysMenus, when it is defined,
190
+ // eg: window.et.sysMenus = ['onNewSsh', 'bookmarks', 'openSetting', 'close']
191
+ // available keys: onNewSsh, addTab, bookmarks, history, sessions, layout,
192
+ // openAbout, openSetting, openDevTools, zoom, minimize, maximize, reload,
193
+ // onCheckUpdate, restart, close
194
+ filterMenus = (items) => {
195
+ const { sysMenus } = window.et || {}
196
+ if (!Array.isArray(sysMenus)) {
197
+ return items
198
+ }
199
+ return items.filter(d => d.type === 'hr' || sysMenus.includes(d.key))
171
200
  }
172
201
 
173
202
  renderMenu () {
@@ -225,7 +225,7 @@ export default class Tabs extends Component {
225
225
  return {
226
226
  key: i + '##' + t.id,
227
227
  label: (
228
- <span><TabTitle tab={t} /></span>
228
+ <span><TabTitle tab={t} config={this.props.config} /></span>
229
229
  ),
230
230
  onClick: () => this.handleClickMenu({ key: i + '##' + t.id })
231
231
  }
@@ -423,7 +423,7 @@ export default class Tabs extends Component {
423
423
  return {
424
424
  key: i + '##' + t.id,
425
425
  label: (
426
- <span><TabTitle tab={t} /></span>
426
+ <span><TabTitle tab={t} config={this.props.config} /></span>
427
427
  ),
428
428
  onClick: () => this.handleClickMenu({ key: i + '##' + t.id })
429
429
  }
@@ -1,7 +1,7 @@
1
1
  import createName from '../../common/create-title'
2
2
 
3
3
  export default function tabTitle (props) {
4
- const { tab } = props
4
+ const { tab, config } = props
5
5
  const title = createName(tab)
6
6
  const { tabCount, color } = props.tab
7
7
  const styleTag = color
@@ -9,7 +9,8 @@ export default function tabTitle (props) {
9
9
  : {}
10
10
  return (
11
11
  <span className='tab-title'>
12
- <span style={styleTag}>●</span> {tabCount}. {title}
12
+ <span style={styleTag}>●</span>
13
+ {!(config && config.disableTabIndex) ? ` ${tabCount}.` : ''} {title}
13
14
  </span>
14
15
  )
15
16
  }
@@ -561,7 +561,11 @@ class Tab extends Component {
561
561
  )
562
562
  }
563
563
  <span className='tab-title'>
564
- <span className='iblock mg1r tab-count' style={styleTag}>{tabCount}</span>
564
+ {!(config && config.disableTabIndex)
565
+ ? (
566
+ <span className='iblock mg1r tab-count' style={styleTag}>{tabCount}</span>
567
+ )
568
+ : null}
565
569
  {tab.isPinned && <PushpinFilled className='tab-pin' />}
566
570
  <span className='mg1r'>{title}</span>
567
571
  </span>