@electerm/electerm-react 5.3.15 → 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 (149) hide show
  1. package/README.md +246 -12
  2. package/client/common/choose-save-folder.js +21 -2
  3. package/client/common/constants.js +23 -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/bookmark-form/index.jsx +10 -8
  29. package/client/components/common/item-filter.jsx +76 -0
  30. package/client/components/common/item-filter.styl +54 -0
  31. package/client/components/common/lazy-boundary.jsx +34 -0
  32. package/client/components/common/opacity.jsx +3 -1
  33. package/client/components/footer/footer-entry.jsx +35 -5
  34. package/client/components/footer/footer.styl +8 -1
  35. package/client/components/layout/layout.jsx +23 -31
  36. package/client/components/layout/pixed.js +3 -0
  37. package/client/components/main/main.jsx +80 -28
  38. package/client/components/main/term-fullscreen.styl +9 -3
  39. package/client/components/quick-commands/qm.styl +3 -2
  40. package/client/components/rdp/file-transfer.js +4 -4
  41. package/client/components/remote-monitor/monitor-details.jsx +458 -0
  42. package/client/components/remote-monitor/monitor-details.styl +87 -0
  43. package/client/components/remote-monitor/monitor-model.js +764 -0
  44. package/client/components/remote-monitor/remote-monitor-bar-entry.jsx +18 -0
  45. package/client/components/remote-monitor/remote-monitor-bar.jsx +344 -0
  46. package/client/components/remote-monitor/remote-monitor-bar.styl +106 -0
  47. package/client/components/remote-monitor/session-monitor.js +361 -0
  48. package/client/components/remote-monitor/use-monitor-details.js +10 -0
  49. package/client/components/remote-monitor/visibility.js +18 -0
  50. package/client/components/session/session-control.jsx +29 -2
  51. package/client/components/session/session.jsx +40 -18
  52. package/client/components/session/sessions.jsx +5 -2
  53. package/client/components/setting-panel/col.jsx +32 -6
  54. package/client/components/setting-panel/list.styl +12 -0
  55. package/client/components/setting-panel/setting-common.jsx +25 -56
  56. package/client/components/setting-panel/setting-mobile-breadcrumb.jsx +29 -0
  57. package/client/components/setting-panel/setting-modal.jsx +64 -44
  58. package/client/components/setting-panel/setting-terminal.jsx +7 -2
  59. package/client/components/setting-panel/setting-wrap.jsx +0 -3
  60. package/client/components/setting-panel/setting-wrap.styl +80 -11
  61. package/client/components/setting-panel/setting.styl +1 -1
  62. package/client/components/setting-panel/tab-triggers.jsx +71 -0
  63. package/client/components/sftp/file-compare-modal.jsx +6 -3
  64. package/client/components/sftp/file-item.jsx +5 -3
  65. package/client/components/sftp/owner-list.js +11 -2
  66. package/client/components/side-panel-r/side-panel-r.jsx +2 -2
  67. package/client/components/sidebar/index.jsx +28 -21
  68. package/client/components/sidebar/left-sidebar-icons.jsx +1 -1
  69. package/client/components/sidebar/sidebar.styl +16 -0
  70. package/client/components/sys-menu/menu-btn.jsx +30 -1
  71. package/client/components/tabs/index.jsx +2 -2
  72. package/client/components/tabs/tab-title.jsx +3 -2
  73. package/client/components/tabs/tab.jsx +5 -1
  74. package/client/components/tabs/tabs.styl +4 -0
  75. package/client/components/terminal/attach-addon-custom.js +295 -49
  76. package/client/components/terminal/automation/index.js +64 -0
  77. package/client/components/terminal/automation/keys.js +49 -0
  78. package/client/components/terminal/automation/stream-matcher.js +82 -0
  79. package/client/components/terminal/automation/strip-ansi.js +19 -0
  80. package/client/components/terminal/automation/trigger-engine.js +233 -0
  81. package/client/components/terminal/automation/trigger-presets.js +110 -0
  82. package/client/components/terminal/command-tracker-addon.js +31 -1
  83. package/client/components/terminal/mixins/index.js +27 -0
  84. package/client/components/terminal/mixins/term-attach.js +123 -0
  85. package/client/components/terminal/mixins/term-config.jsx +84 -0
  86. package/client/components/terminal/mixins/term-content.js +88 -0
  87. package/client/components/terminal/mixins/term-context-menu.jsx +291 -0
  88. package/client/components/terminal/mixins/term-file-drop.js +149 -0
  89. package/client/components/terminal/mixins/term-init.js +122 -0
  90. package/client/components/terminal/mixins/term-log.jsx +96 -0
  91. package/client/components/terminal/mixins/term-resize.js +87 -0
  92. package/client/components/terminal/mixins/term-socket.js +433 -0
  93. package/client/components/terminal/mixins/term-suggestions.js +224 -0
  94. package/client/components/terminal/mixins/term-theme.js +190 -0
  95. package/client/components/terminal/mixins/term-touch.js +136 -0
  96. package/client/components/terminal/shell-detect.js +37 -0
  97. package/client/components/terminal/shell.js +0 -28
  98. package/client/components/terminal/shortcut-bar-entry.jsx +32 -27
  99. package/client/components/terminal/shortcut-bar.jsx +4 -6
  100. package/client/components/terminal/shortcut-bar.styl +4 -0
  101. package/client/components/terminal/ssh-reload-state.js +56 -0
  102. package/client/components/terminal/startup-queue.js +379 -0
  103. package/client/components/terminal/terminal-interactive.jsx +32 -0
  104. package/client/components/terminal/terminal.jsx +102 -2007
  105. package/client/components/terminal/transfer-client-base.js +5 -29
  106. package/client/components/terminal/trzsz-client.js +1 -3
  107. package/client/components/terminal/xmodem-client.js +1 -3
  108. package/client/components/terminal/xterm-loader.js +7 -0
  109. package/client/components/terminal/zmodem-client.js +1 -3
  110. package/client/components/terminal-info/base.jsx +18 -70
  111. package/client/components/terminal-info/terminal-info-entry.jsx +13 -5
  112. package/client/components/terminal-info/terminal-info.jsx +50 -59
  113. package/client/components/terminal-info/terminal-info.styl +6 -49
  114. package/client/components/text-editor/open-text-editor.js +30 -0
  115. package/client/components/text-editor/text-editor-entry.jsx +17 -6
  116. package/client/components/text-editor/text-editor.jsx +14 -0
  117. package/client/components/theme/theme-form.jsx +22 -0
  118. package/client/components/tree-list/bookmark-transport.jsx +7 -31
  119. package/client/components/tree-list/tree-list.styl +13 -0
  120. package/client/components/triggers/trigger-editor.jsx +246 -0
  121. package/client/components/triggers/trigger-form.jsx +200 -0
  122. package/client/components/triggers/trigger-session-modal.jsx +199 -0
  123. package/client/components/triggers/trigger-setting-list.jsx +89 -0
  124. package/client/components/triggers/trigger-transport.jsx +60 -0
  125. package/client/components/triggers/triggers-field.jsx +19 -0
  126. package/client/components/widgets/widgets-list.jsx +11 -1
  127. package/client/css/mobile.styl +9 -1
  128. package/client/store/common.js +12 -1
  129. package/client/store/init-state.js +14 -0
  130. package/client/store/mcp-handler.js +158 -30
  131. package/client/store/setting.js +20 -0
  132. package/client/store/sidebar.js +12 -0
  133. package/client/store/store.js +2 -0
  134. package/client/store/sync.js +45 -9
  135. package/client/store/system-menu.js +2 -0
  136. package/client/store/tab.js +41 -3
  137. package/client/store/trigger.js +118 -0
  138. package/client/store/watch.js +13 -5
  139. package/client/views/index.pug +3 -0
  140. package/package.json +1 -1
  141. package/client/components/terminal-info/activity.jsx +0 -57
  142. package/client/components/terminal-info/data-cols-parser.jsx +0 -54
  143. package/client/components/terminal-info/disk.jsx +0 -45
  144. package/client/components/terminal-info/format-disks.js +0 -45
  145. package/client/components/terminal-info/network.jsx +0 -121
  146. package/client/components/terminal-info/resource.jsx +0 -99
  147. package/client/components/terminal-info/run-cmd.jsx +0 -263
  148. package/client/components/terminal-info/sys-info.jsx +0 -56
  149. 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 />
@@ -0,0 +1,29 @@
1
+ /**
2
+ * breadcrumb shown at the top of the settings content view on mobile.
3
+ * - parent crumb = the active setting tab, clickable, returns to the menu list
4
+ * - current crumb = the selected item name, plain text
5
+ * `onBack` + labels are props so it stays reusable for other panels
6
+ */
7
+
8
+ import { ArrowLeftOutlined } from '@ant-design/icons'
9
+
10
+ export default function SettingMobileBreadcrumb (props) {
11
+ const {
12
+ onBack,
13
+ parentLabel,
14
+ currentLabel
15
+ } = props
16
+ return (
17
+ <div className='setting-mobile-back'>
18
+ <button
19
+ className='setting-mobile-back-btn'
20
+ onClick={onBack}
21
+ >
22
+ <ArrowLeftOutlined />
23
+ <span className='setting-mobile-back-parent'>{parentLabel}</span>
24
+ </button>
25
+ <span className='setting-mobile-back-sep'>&gt;</span>
26
+ <span className='setting-mobile-back-current elli'>{currentLabel}</span>
27
+ </div>
28
+ )
29
+ }
@@ -7,9 +7,11 @@ 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
- modals
13
+ modals,
14
+ settingPanelMobileBreakpoint
13
15
  } from '../../common/constants'
14
16
  const TabBookmarks = lazy(() => import('./tab-bookmarks'))
15
17
  const TabQuickCommands = lazy(() => import('./tab-quick-commands'))
@@ -17,6 +19,7 @@ const TabSettings = lazy(() => import('./tab-settings'))
17
19
  const TabThemes = lazy(() => import('./tab-themes'))
18
20
  const TabProfiles = lazy(() => import('./tab-profiles'))
19
21
  const TabWidgets = lazy(() => import('./tab-widgets'))
22
+ const TabTriggers = lazy(() => import('./tab-triggers'))
20
23
 
21
24
  const Loading = () => <div style={{ padding: 20, textAlign: 'center' }}><Spin /></div>
22
25
 
@@ -25,6 +28,11 @@ const e = window.translate
25
28
  export default auto(function SettingModalWrap (props) {
26
29
  const selectItem = (item) => {
27
30
  window.store.setSettingItem(item)
31
+ // narrow panels drill into the right col content after picking a menu
32
+ // item — same 800px breakpoint as the setting panel media query
33
+ if (window.store.innerWidth <= settingPanelMobileBreakpoint) {
34
+ window.store.settingMobileView = 'content'
35
+ }
28
36
  }
29
37
 
30
38
  function renderTabs () {
@@ -101,6 +109,11 @@ export default auto(function SettingModalWrap (props) {
101
109
  label: e(settingMap.quickCommands),
102
110
  children: null
103
111
  },
112
+ {
113
+ key: settingMap.triggers,
114
+ label: <span>{e('triggers')} <sup>Beta</sup></span>,
115
+ children: null
116
+ },
104
117
  {
105
118
  key: settingMap.profiles,
106
119
  label: e(settingMap.profiles),
@@ -126,48 +139,56 @@ export default auto(function SettingModalWrap (props) {
126
139
  <Tabs
127
140
  {...tabsProps}
128
141
  />
129
- <Suspense fallback={<Loading />}>
130
- <TabQuickCommands
131
- listProps={props0}
132
- settingItem={settingItem}
133
- formProps={formProps}
134
- store={store}
135
- settingTab={settingTab}
136
- />
137
- <TabBookmarks
138
- treeProps={treeProps}
139
- settingItem={settingItem}
140
- formProps={formProps}
141
- settingTab={settingTab}
142
- />
143
- <TabSettings
144
- listProps={props0}
145
- settingItem={settingItem}
146
- settingTab={settingTab}
147
- store={store}
148
- />
149
- <TabThemes
150
- listProps={props0}
151
- settingItem={settingItem}
152
- formProps={formProps}
153
- store={store}
154
- settingTab={settingTab}
155
- />
156
- <TabProfiles
157
- listProps={props0}
158
- settingItem={settingItem}
159
- formProps={formProps}
160
- store={store}
161
- settingTab={settingTab}
162
- />
163
- <TabWidgets
164
- listProps={props0}
165
- settingItem={settingItem}
166
- formProps={formProps}
167
- store={store}
168
- settingTab={settingTab}
169
- />
170
- </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>
171
192
  </>
172
193
  )
173
194
  }
@@ -188,7 +209,6 @@ export default auto(function SettingModalWrap (props) {
188
209
  visible={show}
189
210
  useSystemTitleBar={useSystemTitleBar}
190
211
  innerWidth={innerWidth}
191
- isMobile={props.store.isMobile}
192
212
  >
193
213
  {renderTabs()}
194
214
  </SettingModal>
@@ -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}
@@ -16,9 +16,6 @@ export default class SettingWrap extends Component {
16
16
  }
17
17
 
18
18
  renderRightClose () {
19
- if (this.props.isMobile) {
20
- return null
21
- }
22
19
  return (
23
20
  <CloseCircleOutlined
24
21
  className='close-setting-wrap-icon close-setting-wrap'
@@ -67,34 +67,103 @@
67
67
  .setting-col
68
68
  ::-webkit-scrollbar
69
69
  width 0
70
- @media (max-width: 1100px)
70
+ @media (max-width: 800px)
71
71
  .setting-col
72
72
  position fixed
73
73
  top 128px
74
74
  bottom 0
75
75
  left var(--left-side-bar-width, 43px)
76
76
  right 0
77
- overflow-y scroll
77
+ display flex
78
+ flex-direction column
79
+ overflow hidden
78
80
  .setting-row
79
81
  position static
80
82
  top auto
83
+ // right close button is redundant with the left one on mobile
84
+ .close-setting-wrap:not(.alt-close-setting-wrap)
85
+ display none
86
+ // menu view by default: the left col list fills the panel
81
87
  .setting-row-left
88
+ flex 1
89
+ min-height 0
90
+ overflow-y auto
91
+ position static
92
+ top auto
93
+ bottom auto
82
94
  left auto
83
95
  width auto
84
- bottom auto
85
- padding 138px 10px 10px 10px
96
+ padding 10px
86
97
  .setting-row-right
87
- left auto
98
+ display none
99
+ position static
100
+ top auto
88
101
  right auto
89
102
  bottom auto
90
- top auto
103
+ left auto
104
+ width auto
91
105
  padding 10px
92
- position static
93
- overflow visible
94
- .setting-col-content
95
- overflow visible
106
+ // content view: fixed breadcrumb on top, right col content scrolls below
107
+ .setting-view-content
108
+ .setting-mobile-back
109
+ display flex
110
+ .setting-row-left
111
+ display none
112
+ .setting-row-right
113
+ display flex
114
+ flex-direction column
115
+ flex 1
116
+ min-height 0
117
+ overflow hidden
118
+ position static
119
+ top auto
120
+ right auto
121
+ bottom auto
122
+ left auto
123
+ width auto
124
+ padding 10px
125
+ .setting-col-content
126
+ flex 1
127
+ overflow-y auto
128
+ min-height 0
96
129
  .setting-tabs-bookmarks
97
130
  .setting-row-left
98
131
  width auto
99
132
  .setting-row-right
100
- left auto
133
+ left auto
134
+
135
+ // breadcrumb used on the narrow (mobile) content view; hidden by default and
136
+ // re-shown by `.setting-view-content` inside the 800px media query above
137
+ .setting-mobile-back
138
+ display none
139
+ flex-shrink 0
140
+ align-items center
141
+ gap 4px
142
+ padding 8px 10px
143
+ background var(--main)
144
+ border-bottom 1px solid var(--main-light)
145
+ z-index 10
146
+ .setting-mobile-back-btn
147
+ display flex
148
+ align-items center
149
+ gap 4px
150
+ height 24px
151
+ padding 0 6px
152
+ font-size 13px
153
+ color var(--primary)
154
+ background transparent
155
+ border 0
156
+ border-radius 4px
157
+ cursor pointer
158
+ &:hover
159
+ background var(--primary)
160
+ color var(--primary-contrast)
161
+ .setting-mobile-back-sep
162
+ color var(--text-dark)
163
+ font-size 13px
164
+ line-height 24px
165
+ padding 0 2px
166
+ .setting-mobile-back-current
167
+ font-size 13px
168
+ min-width 0
169
+ max-width 60%
@@ -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)