@electerm/electerm-react 5.0.7 → 5.1.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 (48) hide show
  1. package/client/common/constants.js +2 -0
  2. package/client/common/default-setting.js +10 -1
  3. package/client/common/left-sidebar-icon-defs.js +36 -0
  4. package/client/components/ai/ai-config-props.js +8 -0
  5. package/client/components/bookmark-form/common/fields.jsx +3 -2
  6. package/client/components/bookmark-form/common/ssh-agent.jsx +3 -2
  7. package/client/components/bookmark-form/common/x11.jsx +2 -2
  8. package/client/components/common/logo-elem.jsx +1 -1
  9. package/client/components/common/logo.styl +8 -2
  10. package/client/components/common/switch.jsx +63 -0
  11. package/client/components/common/switch.styl +7 -0
  12. package/client/components/footer/cmd-history.jsx +10 -8
  13. package/client/components/footer/cmd-history.styl +8 -0
  14. package/client/components/layout/layout.jsx +8 -5
  15. package/client/components/main/main.jsx +7 -2
  16. package/client/components/profile/profile-form-elem.jsx +2 -2
  17. package/client/components/quick-commands/quick-commands-box.jsx +2 -19
  18. package/client/components/quick-commands/quick-commands-form-elem.jsx +2 -2
  19. package/client/components/quick-commands/quick-commands-select.jsx +10 -20
  20. package/client/components/rdp/rdp-session.jsx +3 -4
  21. package/client/components/setting-panel/setting-common.jsx +9 -5
  22. package/client/components/setting-panel/setting-left-sidebar-icons.jsx +54 -0
  23. package/client/components/setting-panel/setting-terminal.jsx +3 -4
  24. package/client/components/setting-sync/data-import.jsx +3 -4
  25. package/client/components/setting-sync/setting-sync-form.jsx +3 -2
  26. package/client/components/sftp/file-compare-modal.jsx +1 -1
  27. package/client/components/sidebar/history.jsx +9 -11
  28. package/client/components/sidebar/index.jsx +22 -71
  29. package/client/components/sidebar/left-sidebar-icons.jsx +148 -0
  30. package/client/components/sidebar/sidebar.styl +6 -0
  31. package/client/components/sidebar/transfer-list.jsx +1 -1
  32. package/client/components/spice/spice-session.jsx +4 -5
  33. package/client/components/tabs/no-session.jsx +1 -1
  34. package/client/components/tabs/no-session.styl +31 -8
  35. package/client/components/terminal/shortcut-bar-defs.js +131 -0
  36. package/client/components/terminal/shortcut-bar-edit.jsx +205 -0
  37. package/client/components/terminal/shortcut-bar-entry.jsx +34 -0
  38. package/client/components/terminal/shortcut-bar.jsx +150 -0
  39. package/client/components/terminal/shortcut-bar.styl +186 -0
  40. package/client/components/terminal/terminal-color-query.mjs +47 -0
  41. package/client/components/terminal/terminal.jsx +39 -1
  42. package/client/components/terminal/terminal.styl +8 -0
  43. package/client/components/terminal-info/base.jsx +5 -7
  44. package/client/components/widgets/widget-form.jsx +3 -2
  45. package/client/css/mobile.styl +18 -6
  46. package/client/store/init-state.js +3 -0
  47. package/client/store/mcp-handler.js +29 -1
  48. package/package.json +1 -1
@@ -79,6 +79,8 @@ export const authTypeMap = buildConst([
79
79
 
80
80
  export const footerHeight = 36
81
81
  export const quickCommandBoxHeight = 180
82
+ export const shortcutBarHeight = 44
83
+ export const shortcutBarLsKey = 'shortcut-bar-buttons'
82
84
  export const isWin = typeof window.et.isWin === 'undefined' ? window.pre.isWin : window.et.isWin
83
85
  export const isMac = typeof window.et.isMac === 'undefined' ? window.pre.isMac : window.et.isMac
84
86
  export const isMacJs = /Macintosh|Mac|Mac OS|MacIntel|MacPPC|Mac68K/gi.test(window.navigator.userAgent)
@@ -79,5 +79,14 @@ export default {
79
79
  allowMultiInstance: false,
80
80
  disableDeveloperTool: false,
81
81
  dragDropBehavior: 'ask',
82
- switchTabOnHover: false
82
+ switchTabOnHover: false,
83
+ leftSideBarIcons: [
84
+ 'newBookmark',
85
+ 'quickConnect',
86
+ 'bookmarks',
87
+ 'terminalThemes',
88
+ 'setting',
89
+ 'settingSync',
90
+ 'widgets'
91
+ ]
83
92
  }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * customizable icons for the far-left sidebar.
3
+ *
4
+ * `text` is a locale key passed to window.translate. The "about",
5
+ * "hide sidebar" and "file transfer" icons are intentionally excluded —
6
+ * they are always visible / conditionally rendered and not part of the
7
+ * customizable set.
8
+ *
9
+ * order of `leftSidebarIconOptions` is the default order shown in the sidebar.
10
+ */
11
+ export const leftSidebarIconOptions = [
12
+ { id: 'newBookmark', text: 'newBookmark' },
13
+ { id: 'quickConnect', text: 'quickConnect' },
14
+ { id: 'bookmarks', text: 'bookmarks' },
15
+ { id: 'terminalThemes', text: 'terminalThemes' },
16
+ { id: 'setting', text: 'setting' },
17
+ { id: 'settingSync', text: 'settingSync' },
18
+ { id: 'widgets', text: 'widgets' }
19
+ ]
20
+
21
+ // default ordered list of icon ids — mirrors the order above
22
+ export const defaultLeftSideBarIcons = leftSidebarIconOptions.map(o => o.id)
23
+
24
+ // valid ids, for sanitizing persisted/synced config
25
+ export const leftSidebarIconIds = new Set(leftSidebarIconOptions.map(o => o.id))
26
+
27
+ /**
28
+ * returns a clean, ordered list of icon ids from a possibly-untrusted value:
29
+ * unknown ids dropped, missing default ids left to the user's choice.
30
+ */
31
+ export function normalizeLeftSideBarIcons (value) {
32
+ if (!Array.isArray(value)) {
33
+ return [...defaultLeftSideBarIcons]
34
+ }
35
+ return value.filter(id => leftSidebarIconIds.has(id))
36
+ }
@@ -19,6 +19,14 @@ export const defaultAIPresets = [
19
19
  modelAI: 'deepseek-ai/deepseek-v4-flash',
20
20
  authHeaderNameAI: 'Authorization: Bearer'
21
21
  },
22
+ {
23
+ id: 'ApiSmart',
24
+ nameAI: 'ApiSmart',
25
+ baseURLAI: 'https://api.apismart.ai/v1',
26
+ apiPathAI: '/chat/completions',
27
+ modelAI: 'DEEPSEEK_V4_FLASH',
28
+ authHeaderNameAI: 'Authorization: Bearer'
29
+ },
22
30
  {
23
31
  id: 'deepseek',
24
32
  nameAI: 'DeepSeek',
@@ -3,9 +3,10 @@
3
3
  * Maps field types to React components
4
4
  */
5
5
  import React from 'react'
6
- import { Form, Input, InputNumber, Switch, Select, AutoComplete, Alert, Radio } from 'antd'
6
+ import { Form, Input, InputNumber, Select, AutoComplete, Alert, Radio } from 'antd'
7
7
  import { ColorPickerItem } from './color-picker-item.jsx'
8
8
  import Password from '../../common/password.jsx'
9
+ import SwitchLabel from '../../common/switch.jsx'
9
10
  import InputAutoFocus from '../../common/input-auto-focus.jsx'
10
11
  import ProxyField from './proxy.jsx'
11
12
  import X11Field from './x11.jsx'
@@ -66,7 +67,7 @@ export function renderFormItem (item, formItemLayout, form, ctxProps, index) {
66
67
  control = <InputNumber min={1} max={65535} step={1} {...item.props} />
67
68
  break
68
69
  case 'switch':
69
- control = <Switch {...item.props} />
70
+ control = <SwitchLabel {...item.props} />
70
71
  break
71
72
  case 'select':
72
73
  control = <Select options={item.options} {...item.props} />
@@ -1,5 +1,6 @@
1
1
  import React from 'react'
2
- import { Form, Input, Switch, Space } from 'antd'
2
+ import { Form, Input, Space } from 'antd'
3
+ import SwitchLabel from '../../common/switch'
3
4
  import HelpIcon from '../../common/help-icon'
4
5
  import { formItemLayout } from '../../../common/form-layout'
5
6
 
@@ -18,7 +19,7 @@ export default function SshAgent () {
18
19
  valuePropName='checked'
19
20
  noStyle
20
21
  >
21
- <Switch />
22
+ <SwitchLabel />
22
23
  </FormItem>
23
24
  <FormItem
24
25
  name='sshAgent'
@@ -2,11 +2,11 @@
2
2
  * bookmark form
3
3
  */
4
4
  import {
5
- Switch,
6
5
  Form,
7
6
  Button,
8
7
  Select
9
8
  } from 'antd'
9
+ import SwitchLabel from '../../common/switch'
10
10
  import { formItemLayout } from '../../../common/form-layout'
11
11
 
12
12
  const FormItem = Form.Item
@@ -153,7 +153,7 @@ export default function renderX11 ({ form }) {
153
153
  name='x11'
154
154
  valuePropName='checked'
155
155
  >
156
- <Switch />
156
+ <SwitchLabel />
157
157
  </FormItem>
158
158
  </>
159
159
  )
@@ -9,7 +9,7 @@ import './logo.styl'
9
9
 
10
10
  export default function LogoElem () {
11
11
  return (
12
- <h1 className='mg3y font50'>
12
+ <h1 className='logo-elem mg3y font50'>
13
13
  <span className='iblock pd3 morph-shape mg1l mg1r'>
14
14
  <img src={logoPath2} className='iblock mwm-100 mg1l mg1r logo-filter logo-img' />
15
15
  </span>
@@ -3,8 +3,14 @@
3
3
  .logo-img-small
4
4
  height 28px
5
5
  border-radius 28px
6
- @media (max-width: 500px)
6
+ // the h1 vertical margin so the logo stops dominating the no-session panel.
7
+ .is-mobile
8
+ .logo-elem
9
+ margin-top 8px
10
+ margin-bottom 8px
11
+ .morph-shape
12
+ padding 6px
7
13
  .logo-img
8
- height 40px
14
+ height 32px
9
15
  .logo-img-small
10
16
  height 20px
@@ -0,0 +1,63 @@
1
+ /**
2
+ * antd Switch wrapper.
3
+ *
4
+ * `checkedChildren` / `unCheckedChildren` cram the label text inside the
5
+ * toggle, which renders poorly on small / touch screens. This wrapper drops
6
+ * those props and renders an optional text label as a sibling `<span>`
7
+ * instead. Clicking the label toggles the switch by default.
8
+ *
9
+ * Passes every other prop (checked, onChange, size, disabled, ref, ...) through
10
+ * to the underlying antd Switch, so it is a drop-in replacement and is safe
11
+ * inside antd Form.Item (`valuePropName='checked'`).
12
+ */
13
+
14
+ import { memo } from 'react'
15
+ import { Switch } from 'antd'
16
+ import './switch.styl'
17
+
18
+ function SwitchLabel (props) {
19
+ const {
20
+ label,
21
+ labelPosition = 'after',
22
+ labelClickToggle = true,
23
+ className = '',
24
+ labelClassName = '',
25
+ checked,
26
+ onChange,
27
+ ref,
28
+ ...rest
29
+ } = props
30
+ const hasLabel = label !== undefined && label !== null && label !== ''
31
+ const onLabelClick = labelClickToggle && onChange
32
+ ? () => onChange(!checked)
33
+ : undefined
34
+ const labelEl = hasLabel
35
+ ? (
36
+ <span
37
+ className={
38
+ 'switch-label-text' +
39
+ (labelPosition === 'before' ? ' mg1r' : ' mg1l') +
40
+ (labelClickToggle ? ' pointer' : '') +
41
+ (labelClassName ? ' ' + labelClassName : '')
42
+ }
43
+ onClick={onLabelClick}
44
+ >
45
+ {label}
46
+ </span>
47
+ )
48
+ : null
49
+ return (
50
+ <span className={'switch-label-wrap' + (className ? ' ' + className : '')}>
51
+ {labelPosition === 'before' ? labelEl : null}
52
+ <Switch
53
+ checked={checked}
54
+ onChange={onChange}
55
+ ref={ref}
56
+ {...rest}
57
+ />
58
+ {labelPosition === 'after' ? labelEl : null}
59
+ </span>
60
+ )
61
+ }
62
+
63
+ export default memo(SwitchLabel)
@@ -0,0 +1,7 @@
1
+ .switch-label-wrap
2
+ display inline-flex
3
+ align-items center
4
+ vertical-align middle
5
+
6
+ .switch-label-text
7
+ user-select none
@@ -3,8 +3,9 @@
3
3
  */
4
4
 
5
5
  import { useState, useEffect } from 'react'
6
- import { Button, Empty, Popover, Switch } from 'antd'
6
+ import { Button, Empty, Popover } from 'antd'
7
7
  import { auto } from 'manate/react'
8
+ import SwitchLabel from '../common/switch'
8
9
  import { copy } from '../../common/clipboard'
9
10
  import { HistoryOutlined, DeleteOutlined, CopyOutlined, UnorderedListOutlined } from '@ant-design/icons'
10
11
  import InputAutoFocus from '../common/input-auto-focus'
@@ -111,13 +112,14 @@ export default auto(function CmdHistory (props) {
111
112
  }
112
113
  return (
113
114
  <div className='cmd-history-header pd2b'>
114
- <Switch
115
- checkedChildren={e('sortByFrequency')}
116
- unCheckedChildren={e('sortByFrequency')}
117
- checked={sortByFrequency}
118
- onChange={handleSortByFrequencyChange}
119
- size='small'
120
- />
115
+ <div className='cmd-history-sort'>
116
+ <SwitchLabel
117
+ checked={sortByFrequency}
118
+ onChange={handleSortByFrequencyChange}
119
+ size='small'
120
+ label={e('sortByFrequency')}
121
+ />
122
+ </div>
121
123
  <UnorderedListOutlined
122
124
  className='cmd-history-clear-icon pointer clear-ai-icon icon-hover'
123
125
  title={e('clear')}
@@ -17,6 +17,14 @@
17
17
  justify-content space-between
18
18
  padding-bottom 8px
19
19
 
20
+ .cmd-history-sort
21
+ display flex
22
+ align-items center
23
+ gap 6px
24
+
25
+ .cmd-history-sort-label
26
+ user-select none
27
+
20
28
  .cmd-history-clear-icon
21
29
  font-size 14px
22
30
  padding 4px
@@ -4,7 +4,8 @@ import TabsWrap from '../tabs/index'
4
4
  import {
5
5
  splitConfig,
6
6
  quickCommandBoxHeight,
7
- footerHeight
7
+ footerHeight,
8
+ shortcutBarHeight
8
9
  } from '../../common/constants'
9
10
  import layoutAlg from './layout-alg'
10
11
  import calcSessionSize from './session-size-alg'
@@ -41,9 +42,10 @@ export default auto(function Layout (props) {
41
42
  rightPanelPinned,
42
43
  rightPanelWidth,
43
44
  resizeTrigger,
44
- inActiveTerminal
45
+ inActiveTerminal,
46
+ shortcutBarVisible
45
47
  } = props.store
46
- const h = height - footerHeight - (inActiveTerminal && pinnedQuickCommandBar ? quickCommandBoxHeight : 0) + resizeTrigger
48
+ const h = height - footerHeight - (inActiveTerminal && pinnedQuickCommandBar ? quickCommandBoxHeight : 0) - (shortcutBarVisible ? shortcutBarHeight : 0) + resizeTrigger
47
49
  const l = pinned ? leftSideBarWidth + leftSidePanelWidth : leftSideBarWidth
48
50
  const r = rightPanelVisible && rightPanelPinned ? rightPanelWidth : 0
49
51
  return {
@@ -65,14 +67,15 @@ export default auto(function Layout (props) {
65
67
  rightPanelVisible,
66
68
  rightPanelPinned,
67
69
  rightPanelWidth,
68
- pinned
70
+ pinned,
71
+ shortcutBarVisible
69
72
  } = props.store
70
73
  const l = pinned ? leftSidePanelWidth : 0
71
74
  const r = rightPanelPinned && rightPanelVisible ? rightPanelWidth : 0
72
75
  // account for the far-left icon bar (sidebarWidth - 1px border on desktop;
73
76
  // 0 when the bar is hidden on mobile)
74
77
  const w = width - l - r - (leftSideBarWidth > 0 ? leftSideBarWidth - 1 : 0)
75
- const h = height - footerHeight - (pinnedQuickCommandBar ? quickCommandBoxHeight : 0)
78
+ const h = height - footerHeight - (pinnedQuickCommandBar ? quickCommandBoxHeight : 0) - (shortcutBarVisible ? shortcutBarHeight : 0)
76
79
  return layoutAlg(layout, w, h)
77
80
  }
78
81
  const layoutSize = calcLayoutStyle()
@@ -42,6 +42,7 @@ import { pick } from 'lodash-es'
42
42
  import deepCopy from 'json-deep-copy'
43
43
  import './wrapper.styl'
44
44
  import TerminalInfo from '../terminal-info/terminal-info-entry'
45
+ import ShortcutBarEntry from '../terminal/shortcut-bar-entry'
45
46
  import '../../common/fs.js'
46
47
  import './term-fullscreen.styl'
47
48
 
@@ -99,7 +100,8 @@ export default auto(function Index (props) {
99
100
  transferToConfirm,
100
101
  openResolutionEdit,
101
102
  rightPanelTitle,
102
- rightPanelTab
103
+ rightPanelTab,
104
+ widgetInstances
103
105
  } = store
104
106
  const upgradeInfo = deepCopy(store.upgradeInfo)
105
107
  const cls = classnames({
@@ -181,7 +183,9 @@ export default auto(function Index (props) {
181
183
  fileTransfers: copiedTransfer,
182
184
  transferHistory: copiedHistory,
183
185
  upgradeInfo,
184
- pinned
186
+ pinned,
187
+ leftSideBarIcons: config.leftSideBarIcons,
188
+ widgetInstancesLength: widgetInstances.length
185
189
  }
186
190
 
187
191
  const infoModalProps = {
@@ -311,6 +315,7 @@ export default auto(function Index (props) {
311
315
  <BookmarkFromHistoryModal />
312
316
  <NotificationContainer />
313
317
  <BatchOpRunner />
318
+ <ShortcutBarEntry store={store} />
314
319
  {!isAIDisabled() && <AIConfigModal store={store} />}
315
320
  <UnixTimestampTooltip />
316
321
  </div>
@@ -1,10 +1,10 @@
1
1
  import { useState } from 'react'
2
2
  import {
3
3
  Form,
4
- Switch,
5
4
  Button
6
5
  } from 'antd'
7
6
  import message from '../common/message'
7
+ import SwitchLabel from '../common/switch'
8
8
  import InputAutoFocus from '../common/input-auto-focus'
9
9
  import { formItemLayout } from '../../common/form-layout'
10
10
  import HelpIcon from '../common/help-icon'
@@ -90,7 +90,7 @@ export default function ProfileFormElem (props) {
90
90
  name='isDefault'
91
91
  valuePropName='checked'
92
92
  >
93
- <Switch />
93
+ <SwitchLabel />
94
94
  </FormItem>
95
95
  <ProfileTabs {...tabsProps} />
96
96
  <FormItem>
@@ -2,7 +2,7 @@
2
2
  * quick commands footer selection wrap
3
3
  */
4
4
 
5
- import { useState, useRef } from 'react'
5
+ import { useState } from 'react'
6
6
  import { quickCommandLabelsLsKey, pinnedQuickCommandBarKey } from '../../common/constants'
7
7
  import { sortBy } from 'lodash-es'
8
8
  import { Button, Input, Select, Space, Flex } from 'antd'
@@ -25,21 +25,6 @@ const { Option } = Select
25
25
  export default function QuickCommandsFooterBox (props) {
26
26
  const [keyword, setKeyword] = useState('')
27
27
  const [label, setLabel] = useState(ls.getItem(quickCommandLabelsLsKey, ''))
28
- const timer = useRef(null)
29
-
30
- function handleMouseLeave () {
31
- timer.current = setTimeout(() => {
32
- toggle(false)
33
- }, 500)
34
- }
35
-
36
- function handleMouseEnter () {
37
- clearTimeout(timer.current)
38
- }
39
-
40
- function toggle (openQuickCommandBar) {
41
- window.store.openQuickCommandBar = openQuickCommandBar
42
- }
43
28
 
44
29
  function handleTogglePinned () {
45
30
  const current = !window.store.pinnedQuickCommandBar
@@ -209,9 +194,7 @@ export default function QuickCommandsFooterBox (props) {
209
194
  className: 'qm-wrap-tooltip',
210
195
  style: {
211
196
  left: w
212
- },
213
- onMouseLeave: handleMouseLeave,
214
- onMouseEnter: handleMouseEnter
197
+ }
215
198
  }
216
199
  return (
217
200
  <div
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  Button,
3
- Switch,
4
3
  Form,
5
4
  Select,
6
5
  Input
7
6
  } from 'antd'
8
7
  import message from '../common/message'
8
+ import SwitchLabel from '../common/switch'
9
9
  import { useState } from 'react'
10
10
  import generate from '../../common/uid'
11
11
  import InputAutoFocus from '../common/input-auto-focus'
@@ -162,7 +162,7 @@ export default function QuickCommandForm (props) {
162
162
  name='inputOnly'
163
163
  valuePropName='checked'
164
164
  >
165
- <Switch />
165
+ <SwitchLabel />
166
166
  </FormItem>
167
167
  <FormItem>
168
168
  <p>
@@ -1,5 +1,10 @@
1
1
  /**
2
2
  * quick commands footer selection
3
+ *
4
+ * Click to toggle the quick-command bar. Click-based so it works identically
5
+ * on touch (mobile) and mouse (desktop) — the old hover-to-open model was
6
+ * unreliable on Android, where a tap synthesizes mouseleave and cancels the
7
+ * open timer.
3
8
  */
4
9
 
5
10
  import { PureComponent } from 'react'
@@ -7,33 +12,18 @@ import { Button } from 'antd'
7
12
  import './qm.styl'
8
13
 
9
14
  export default class QuickCommandsFooter extends PureComponent {
10
- componentWillUnmount () {
11
- clearTimeout(this.timer)
12
- this.timer = null
13
- }
14
-
15
- handleOpen = () => {
16
- this.timer = setTimeout(this.act, 500)
17
- }
18
-
19
- act = () => {
20
- window.store.openQuickCommandBar = true
21
- }
22
-
23
- handleMouseLeave = () => {
24
- clearTimeout(this.timer)
15
+ handleClick = () => {
16
+ const { store } = window
17
+ store.openQuickCommandBar = !store.openQuickCommandBar
25
18
  }
26
19
 
27
20
  render () {
28
21
  return (
29
- <div
30
- className='fleft relative quick-command-trigger-wrap'
31
- onMouseEnter={this.handleOpen}
32
- onMouseLeave={this.handleMouseLeave}
33
- >
22
+ <div className='fleft relative quick-command-trigger-wrap'>
34
23
  <Button
35
24
  size='small'
36
25
  type='text'
26
+ onClick={this.handleClick}
37
27
  >
38
28
  Q
39
29
  </Button>
@@ -15,7 +15,6 @@ import {
15
15
  import {
16
16
  Spin,
17
17
  Select,
18
- Switch,
19
18
  Tooltip
20
19
  } from 'antd'
21
20
  import * as ls from '../../common/safe-local-storage'
@@ -23,6 +22,7 @@ import scanCode from './code-scan'
23
22
  import resolutions from './resolutions'
24
23
  import { readClipboardAsync } from '../../common/clipboard'
25
24
  import RemoteFloatControl from '../common/remote-float-control'
25
+ import SwitchLabel from '../common/switch'
26
26
  import HelpIcon from '../common/help-icon'
27
27
  import { FileTransferManager, createFileLogger } from './file-transfer'
28
28
  import { notification } from '../common/notification'
@@ -634,8 +634,7 @@ export default class RdpSession extends PureComponent {
634
634
  const scaleProps = {
635
635
  checked: this.state.scaleViewport,
636
636
  onChange: this.handleScaleViewChange,
637
- unCheckedChildren: window.translate('scaleViewport'),
638
- checkedChildren: window.translate('scaleViewport'),
637
+ label: window.translate('scaleViewport'),
639
638
  className: 'mg1l'
640
639
  }
641
640
  const uploadTitle = window.translate('upload') || 'Upload files to remote'
@@ -671,7 +670,7 @@ export default class RdpSession extends PureComponent {
671
670
  className='mg2r mg1l pointer'
672
671
  />
673
672
  {this.renderInfo()}
674
- <Switch
673
+ <SwitchLabel
675
674
  {...scaleProps}
676
675
  />
677
676
  <Tooltip title={uploadTitle}>
@@ -7,9 +7,9 @@ import {
7
7
  } from '@ant-design/icons'
8
8
  import message from '../common/message'
9
9
  import { notification } from '../common/notification'
10
+ import SwitchLabel from '../common/switch'
10
11
  import {
11
12
  Select,
12
- Switch,
13
13
  Button,
14
14
  Table,
15
15
  Space,
@@ -34,6 +34,7 @@ import delay from '../../common/wait.js'
34
34
  import isColorDark from '../../common/is-color-dark'
35
35
  import DeepLinkControl from './deep-link-control'
36
36
  import HotkeySetting from './hotkey'
37
+ import SettingLeftSidebarIcons from './setting-left-sidebar-icons'
37
38
  import './setting.styl'
38
39
 
39
40
  const { Option } = Select
@@ -192,10 +193,9 @@ export default class SettingCommon extends Component {
192
193
  const checked = !!this.props.config[name]
193
194
  return (
194
195
  <div className='pd2b' key={'rt' + name}>
195
- <Switch
196
+ <SwitchLabel
196
197
  checked={checked}
197
- checkedChildren={e(name)}
198
- unCheckedChildren={e(name)}
198
+ label={e(name)}
199
199
  onChange={v => this.onChangeValue(v, name)}
200
200
  />
201
201
  {isNumber(extra) ? null : extra}
@@ -372,7 +372,7 @@ export default class SettingCommon extends Component {
372
372
  style={{ body: { style } }}
373
373
  />
374
374
  </span>
375
- <Switch
375
+ <SwitchLabel
376
376
  checked={enableGlobalProxy}
377
377
  onChange={v => {
378
378
  this.onChangeValue(v, 'enableGlobalProxy')
@@ -471,6 +471,10 @@ export default class SettingCommon extends Component {
471
471
  return (
472
472
  <div className='form-wrap pd1y pd2x'>
473
473
  <h2>{e('settings')}</h2>
474
+ <SettingLeftSidebarIcons
475
+ config={props.config}
476
+ store={props.store}
477
+ />
474
478
  <HotkeySetting
475
479
  {...hotkeyProps}
476
480
  />
@@ -0,0 +1,54 @@
1
+ /**
2
+ * editor (embedded in the common settings page) for the customizable
3
+ * far-left sidebar icons. The select takes effect immediately — order of the
4
+ * selected ids is the order shown in the sidebar.
5
+ */
6
+
7
+ import { Select } from 'antd'
8
+ import {
9
+ leftSidebarIconOptions,
10
+ defaultLeftSideBarIcons
11
+ } from '../../common/left-sidebar-icon-defs'
12
+
13
+ const { Option } = Select
14
+ const e = window.translate
15
+
16
+ export default function SettingLeftSidebarIcons (props) {
17
+ const { config, store } = props
18
+ const value = Array.isArray(config.leftSideBarIcons) && config.leftSideBarIcons.length
19
+ ? config.leftSideBarIcons
20
+ : [...defaultLeftSideBarIcons]
21
+
22
+ const handleChange = (v) => {
23
+ store.updateConfig({
24
+ leftSideBarIcons: v
25
+ })
26
+ }
27
+
28
+ return (
29
+ <div className='pd2b'>
30
+ <div className='pd1b'>{e('leftSideBarIcons')}</div>
31
+ <Select
32
+ mode='multiple'
33
+ value={value}
34
+ onChange={handleChange}
35
+ popupMatchSelectWidth={false}
36
+ style={{ minWidth: 320, width: '100%' }}
37
+ placeholder={e('leftSideBarIcons')}
38
+ >
39
+ {
40
+ leftSidebarIconOptions.map(opt => {
41
+ return (
42
+ <Option
43
+ key={opt.id}
44
+ value={opt.id}
45
+ >
46
+ {e(opt.text)}
47
+ </Option>
48
+ )
49
+ })
50
+ }
51
+ </Select>
52
+ </div>
53
+ )
54
+ }
@@ -4,9 +4,9 @@ import {
4
4
  LoadingOutlined
5
5
  } from '@ant-design/icons'
6
6
  import message from '../common/message'
7
+ import SwitchLabel from '../common/switch'
7
8
  import {
8
9
  Select,
9
- Switch,
10
10
  Button,
11
11
  AutoComplete,
12
12
  Tooltip,
@@ -123,10 +123,9 @@ export default class SettingTerminal extends Component {
123
123
  const txt = label || e(name)
124
124
  return (
125
125
  <div className={cls} key={'rt' + name}>
126
- <Switch
126
+ <SwitchLabel
127
127
  checked={checked}
128
- checkedChildren={txt}
129
- unCheckedChildren={txt}
128
+ label={txt}
130
129
  onChange={v => this.onChangeValue(v, name)}
131
130
  />
132
131
  </div>