@electerm/electerm-react 2.3.118 → 2.3.136

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 (47) hide show
  1. package/client/common/constants.js +2 -1
  2. package/client/common/db.js +2 -1
  3. package/client/common/download.jsx +15 -0
  4. package/client/common/init-setting-item.js +5 -0
  5. package/client/components/ai/ai-cache.jsx +1 -1
  6. package/client/components/batch-op/batch-op.jsx +1 -1
  7. package/client/components/bookmark-form/common/bookmark-group-tree-format.js +4 -3
  8. package/client/components/bookmark-form/common/ssh-host-selector.jsx +1 -1
  9. package/client/components/bookmark-form/common/ssh-tunnels.jsx +1 -0
  10. package/client/components/bookmark-form/tree-delete.jsx +18 -15
  11. package/client/components/common/input-auto-focus.jsx +16 -3
  12. package/client/components/common/logo-elem.jsx +1 -1
  13. package/client/components/common/password.jsx +1 -1
  14. package/client/components/footer/footer.styl +1 -0
  15. package/client/components/main/main.jsx +4 -1
  16. package/client/components/quick-commands/qm.styl +19 -0
  17. package/client/components/quick-commands/quick-commands-box.jsx +28 -36
  18. package/client/components/quick-commands/quick-commands-list-form.jsx +1 -0
  19. package/client/components/quick-commands/quick-commands-select.jsx +1 -1
  20. package/client/components/session/session.jsx +1 -1
  21. package/client/components/setting-panel/deep-link-control.jsx +1 -0
  22. package/client/components/setting-panel/list.styl +1 -1
  23. package/client/components/setting-panel/setting-common.jsx +1 -0
  24. package/client/components/setting-panel/setting-modal.jsx +13 -0
  25. package/client/components/setting-panel/setting-wrap.jsx +1 -1
  26. package/client/components/setting-panel/start-session-select.jsx +3 -3
  27. package/client/components/setting-panel/tab-widgets.jsx +35 -0
  28. package/client/components/sftp/address-bookmark-item.jsx +1 -1
  29. package/client/components/sftp/permission-render.jsx +1 -1
  30. package/client/components/sidebar/index.jsx +12 -2
  31. package/client/components/sidebar/transfer-history-modal.jsx +1 -1
  32. package/client/components/terminal/terminal-command-dropdown.jsx +2 -2
  33. package/client/components/text-editor/simple-editor.jsx +3 -1
  34. package/client/components/theme/theme-list-item.jsx +4 -3
  35. package/client/components/tree-list/move-item-modal.jsx +60 -9
  36. package/client/components/tree-list/tree-list.jsx +2 -14
  37. package/client/components/vnc/vnc-session.jsx +1 -1
  38. package/client/components/widgets/widget-control.jsx +64 -0
  39. package/client/components/widgets/widget-form.jsx +115 -0
  40. package/client/components/widgets/widget-instance.jsx +46 -0
  41. package/client/components/widgets/widget-instances.jsx +10 -0
  42. package/client/components/widgets/widgets-list.jsx +155 -0
  43. package/client/store/init-state.js +9 -1
  44. package/client/store/setting.js +1 -3
  45. package/client/store/store.js +2 -0
  46. package/client/store/widgets.js +59 -0
  47. package/package.json +1 -1
@@ -101,7 +101,8 @@ export const settingMap = buildConst([
101
101
  'bookmarkGroups',
102
102
  'quickCommands',
103
103
  'addressBookmarks',
104
- 'profiles'
104
+ 'profiles',
105
+ 'widgets'
105
106
  ])
106
107
 
107
108
  export const staticNewItemTabs = new Set([
@@ -25,7 +25,8 @@ const dbAction = (...args) => {
25
25
  */
26
26
  export const dbNames = without(
27
27
  Object.keys(settingMap),
28
- settingMap.setting
28
+ settingMap.setting,
29
+ settingMap.widgets
29
30
  )
30
31
 
31
32
  export const dbNamesForWatch = without(
@@ -5,7 +5,22 @@ import { notification } from 'antd'
5
5
  import ShowItem from '../components/common/show-item'
6
6
  import { chooseSaveDirectory } from './choose-save-folder'
7
7
 
8
+ function downloadForBrowser (filename, text) {
9
+ const blob = new Blob([text], { type: 'text/plain;charset=utf-8' })
10
+ const url = URL.createObjectURL(blob)
11
+ const a = document.createElement('a')
12
+ a.href = url
13
+ a.download = filename
14
+ document.body.appendChild(a)
15
+ a.click()
16
+ document.body.removeChild(a)
17
+ URL.revokeObjectURL(url)
18
+ }
19
+
8
20
  export default async function download (filename, text) {
21
+ if (window.et.isWebApp) {
22
+ return downloadForBrowser(filename, text)
23
+ }
9
24
  const savePath = await chooseSaveDirectory()
10
25
  if (!savePath) {
11
26
  return
@@ -31,5 +31,10 @@ export default (arr, tab) => {
31
31
  id: '',
32
32
  name: e(settingMap.profiles)
33
33
  }
34
+ } else if (tab === settingMap.widgets) {
35
+ return {
36
+ id: '',
37
+ name: e(settingMap.widgets)
38
+ }
34
39
  }
35
40
  }
@@ -31,6 +31,6 @@ export default function AiCache () {
31
31
  </>
32
32
  )
33
33
  return (
34
- <Alert message={msg} type='info' className='mg2y' />
34
+ <Alert title={msg} type='info' className='mg2y' />
35
35
  )
36
36
  }
@@ -673,7 +673,7 @@ export default class BatchOp extends PureComponent {
673
673
  open: showBatchOp,
674
674
  onClose: this.handleCancel,
675
675
  className: 'setting-wrap',
676
- width: innerWidth - sidebarWidth,
676
+ size: innerWidth - sidebarWidth,
677
677
  zIndex: 888,
678
678
  placement: 'left',
679
679
  styles: {
@@ -2,7 +2,7 @@
2
2
  * create bookmark group tree data
3
3
  */
4
4
 
5
- export default (bookmarkGroups = [], disabledId = '', returnMap = false) => {
5
+ export default (bookmarkGroups = [], disabledId = '', returnMap = false, currentParentId = '') => {
6
6
  const btree = new Map(bookmarkGroups.map(d => [d.id, d]))
7
7
  function buildSubCats (id) {
8
8
  const x = btree.get(id)
@@ -12,7 +12,8 @@ export default (bookmarkGroups = [], disabledId = '', returnMap = false) => {
12
12
  const y = {
13
13
  key: x.id,
14
14
  value: x.id,
15
- title: x.title
15
+ title: x.title,
16
+ disabled: x.id === disabledId || x.id === currentParentId
16
17
  }
17
18
  y.children = (x.bookmarkGroupIds || []).map(buildSubCats).filter(d => d)
18
19
  if (!y.children.length) {
@@ -26,7 +27,7 @@ export default (bookmarkGroups = [], disabledId = '', returnMap = false) => {
26
27
  title: d.title,
27
28
  value: d.id,
28
29
  key: d.id,
29
- disabled: d.id === disabledId,
30
+ disabled: d.id === disabledId || d.id === currentParentId,
30
31
  children: (d.bookmarkGroupIds || []).map(buildSubCats).filter(d => d)
31
32
  }
32
33
  return r
@@ -52,7 +52,7 @@ export default function SshHostSelector ({ ips = [], useIp, form, onBlur, onPast
52
52
  <InputAutoFocus
53
53
  name='host'
54
54
  onBlur={props.onBlur}
55
- onPaste={e => props.onPaste(e, form)}
55
+ onPaste={e => onPaste(e, form)}
56
56
  addonBefore={<ColorPickerItem />}
57
57
  />
58
58
  </FormItem>
@@ -209,6 +209,7 @@ export default function renderSshTunnels (props) {
209
209
  form={formChild}
210
210
  onFinish={handleFinish}
211
211
  initialValues={initialValues}
212
+ component='div'
212
213
  >
213
214
  {renderList()}
214
215
  <FormItem
@@ -1,7 +1,8 @@
1
1
  import StartSessionSelect from '../setting-panel/start-session-select'
2
2
  import {
3
3
  Tree,
4
- Button
4
+ Button,
5
+ Space
5
6
  } from 'antd'
6
7
  import { defaultBookmarkGroupId, settingMap } from '../../common/constants'
7
8
  import deepCopy from 'json-deep-copy'
@@ -48,21 +49,23 @@ export default class BookmarkTreeDelete extends StartSessionSelect {
48
49
  const len = checkedKeys.length
49
50
  return (
50
51
  <div>
51
- <div className='pd1'>
52
- <Button
53
- type='primary'
54
- disabled={!len}
55
- onClick={this.handleDel}
56
- >
57
- {e('delSelected')} ({len})
58
- </Button>
59
- <Button
60
- onClick={this.handleCancel}
61
- >
62
- {e('cancel')}
63
- </Button>
52
+ <div className='pd2'>
53
+ <Space.Compact>
54
+ <Button
55
+ type='primary'
56
+ disabled={!len}
57
+ onClick={this.handleDel}
58
+ >
59
+ {e('delSelected')} ({len})
60
+ </Button>
61
+ <Button
62
+ onClick={this.handleCancel}
63
+ >
64
+ {e('cancel')}
65
+ </Button>
66
+ </Space.Compact>
67
+ <Tree {...rProps} />
64
68
  </div>
65
- <Tree {...rProps} />
66
69
  </div>
67
70
  )
68
71
  }
@@ -5,7 +5,7 @@ import {
5
5
  import Password from './password'
6
6
 
7
7
  export default function InputAutoFocus (props) {
8
- const { type, selectall = false, ...rest } = props
8
+ const { type, selectall = false, ref, ...rest } = props
9
9
  const inputRef = useRef(null)
10
10
  const isFirstRender = useRef(true)
11
11
 
@@ -14,7 +14,11 @@ export default function InputAutoFocus (props) {
14
14
  const { value } = props
15
15
  if (value && selectall && isFirstRender.current) {
16
16
  inputRef.current.focus()
17
- inputRef.current.setSelectionRange(0, value.length)
17
+ if (inputRef.current.setSelectionRange) {
18
+ inputRef.current.setSelectionRange(0, value.length)
19
+ } else if (inputRef.current.select) {
20
+ inputRef.current.select()
21
+ }
18
22
  isFirstRender.current = false
19
23
  } else {
20
24
  inputRef.current.focus()
@@ -31,9 +35,18 @@ export default function InputAutoFocus (props) {
31
35
  InputComponent = Input
32
36
  }
33
37
 
38
+ const handleRef = (node) => {
39
+ inputRef.current = node
40
+ if (typeof ref === 'function') {
41
+ ref(node)
42
+ } else if (ref) {
43
+ ref.current = node
44
+ }
45
+ }
46
+
34
47
  return (
35
48
  <InputComponent
36
- ref={inputRef}
49
+ ref={handleRef}
37
50
  {...rest}
38
51
  />
39
52
  )
@@ -17,7 +17,7 @@ export default function LogoElem () {
17
17
  <sup>
18
18
  <img src={logoPath1} className='iblock mwm-100 mg1r logo-img-small' />
19
19
  </sup>
20
- <Tag color='#08c'>{packInfo.version}</Tag>
20
+ <Tag color='#08c' variant='solid'>{packInfo.version}</Tag>
21
21
  </h1>
22
22
  )
23
23
  }
@@ -56,7 +56,7 @@ export default forwardRef(function Password (props, ref) {
56
56
  let capsPrefix = null
57
57
  if (isCapsLockOn) {
58
58
  capsPrefix = (
59
- <Tag color='orange' style={{ marginRight: 4 }}>CAPS</Tag>
59
+ <Tag color='orange' className='mg1r' variant='solid'>CAPS</Tag>
60
60
  )
61
61
  }
62
62
 
@@ -35,6 +35,7 @@
35
35
  .bi-full
36
36
  display none
37
37
  &.bi-show
38
+ line-height 20px
38
39
  .bi-full
39
40
  display inline
40
41
  .bi-compact
@@ -29,6 +29,7 @@ import SshConfigLoadNotify from '../ssh-config/ssh-config-load-notify'
29
29
  import LoadSshConfigs from '../ssh-config/load-ssh-configs'
30
30
  import AIChat from '../ai/ai-chat'
31
31
  import Opacity from '../common/opacity'
32
+ import MoveItemModal from '../tree-list/move-item-modal'
32
33
  import InputContextMenu from '../common/input-context-menu'
33
34
  import { pick } from 'lodash-es'
34
35
  import deepCopy from 'json-deep-copy'
@@ -169,7 +170,8 @@ export default auto(function Index (props) {
169
170
  'isSyncingSetting',
170
171
  'leftSidebarWidth',
171
172
  'transferTab',
172
- 'sidebarPanelTab'
173
+ 'sidebarPanelTab',
174
+ 'openWidgetsModal'
173
175
  ]),
174
176
  fileTransfers: copiedTransfer,
175
177
  transferHistory: copiedHistory,
@@ -273,6 +275,7 @@ export default auto(function Index (props) {
273
275
  <FileInfoModal />
274
276
  <SettingModal store={store} />
275
277
  <BatchOp {...batchOpProps} />
278
+ <MoveItemModal store={store} />
276
279
  <div
277
280
  id='outside-context'
278
281
  >
@@ -1,6 +1,7 @@
1
1
  .qm-list-wrap
2
2
  max-height calc(100vh - 100px)
3
3
  overflow-y scroll
4
+ padding 20px 0 10px 0
4
5
  .qm-wrap-tooltip
5
6
  background var(--main)
6
7
  position absolute
@@ -24,9 +25,27 @@
24
25
  &.name-match
25
26
  &.label-match
26
27
  font-weight bold
28
+
29
+ .qm-search-input
30
+ max-width 200px
27
31
  @media (max-width: 500px)
28
32
  .qm-search-input
29
33
  width 100px
30
34
 
31
35
  .item-list-unit.dragover
32
36
  border: 1px dashed var(--primary)
37
+ .qm-label-select
38
+ min-width 120px
39
+
40
+ .qm-flex
41
+ gap 8px
42
+
43
+ @media (max-width: 768px)
44
+ .qm-flex
45
+ flex-direction column
46
+ align-items flex-start
47
+ .qm-search-input
48
+ max-width none
49
+ width 100%
50
+ .qm-label-select
51
+ width 100%
@@ -5,7 +5,7 @@
5
5
  import { useState, useRef } from 'react'
6
6
  import { quickCommandLabelsLsKey } from '../../common/constants'
7
7
  import { sortBy } from 'lodash-es'
8
- import { Button, Input, Select, Space } from 'antd'
8
+ import { Button, Input, Select, Space, Flex } from 'antd'
9
9
  import * as ls from '../../common/safe-local-storage'
10
10
  import CmdItem from './quick-command-item'
11
11
  import {
@@ -169,14 +169,11 @@ export default function QuickCommandsFooterBox (props) {
169
169
  mode: 'multiple',
170
170
  onChange: handleChangeLabels,
171
171
  placeholder: e('labels'),
172
- className: 'iblock',
173
- style: {
174
- minWidth: '100px'
175
- }
172
+ className: 'qm-label-select'
176
173
  }
177
174
  const tp = pinnedQuickCommandBar
178
175
  ? 'primary'
179
- : 'ghost'
176
+ : 'text'
180
177
  const cls = classNames(
181
178
  'qm-list-wrap',
182
179
  { 'fil-label': !!labels.length },
@@ -197,16 +194,14 @@ export default function QuickCommandsFooterBox (props) {
197
194
  {...qmProps}
198
195
  >
199
196
  <div className='pd2'>
200
- <div className='pd2b fix'>
201
- <span className='fleft'>
202
- <Input.Search
203
- value={keyword}
204
- onChange={handleChange}
205
- placeholder=''
206
- className='iblock qm-search-input'
207
- />
208
- </span>
209
- <span className='fleft mg1l'>
197
+ <Flex justify='space-between' className='qm-flex'>
198
+ <Input.Search
199
+ value={keyword}
200
+ onChange={handleChange}
201
+ placeholder=''
202
+ className='qm-search-input'
203
+ />
204
+ <Flex gap='small'>
210
205
  <Select
211
206
  {...sprops}
212
207
  >
@@ -215,31 +210,28 @@ export default function QuickCommandsFooterBox (props) {
215
210
  )}
216
211
  </Select>
217
212
  <Button
218
- className='mg1l iblock'
219
213
  type={type}
220
214
  onClick={window.store.handleSortByFrequency}
221
215
  >
222
216
  {e('sortByFrequency')}
223
217
  </Button>
224
- </span>
225
- <span className='fright'>
226
- <Space.Compact>
227
- <Button
228
- onClick={handleTogglePinned}
229
- icon={<PushpinOutlined />}
230
- type={tp}
231
- />
232
- <Button
233
- onClick={window.store.handleOpenQuickCommandsSetting}
234
- icon={<EditOutlined />}
235
- />
236
- <Button
237
- onClick={handleClose}
238
- icon={<CloseCircleOutlined />}
239
- />
240
- </Space.Compact>
241
- </span>
242
- </div>
218
+ </Flex>
219
+ <Space.Compact className='mg2l'>
220
+ <Button
221
+ onClick={handleTogglePinned}
222
+ icon={<PushpinOutlined />}
223
+ type={tp}
224
+ />
225
+ <Button
226
+ onClick={window.store.handleOpenQuickCommandsSetting}
227
+ icon={<EditOutlined />}
228
+ />
229
+ <Button
230
+ onClick={handleClose}
231
+ icon={<CloseCircleOutlined />}
232
+ />
233
+ </Space.Compact>
234
+ </Flex>
243
235
  <div className={cls}>
244
236
  {filtered.map(renderItem)}
245
237
  </div>
@@ -88,6 +88,7 @@ export default function renderQm () {
88
88
  return (
89
89
  <Tag
90
90
  title={c.desc}
91
+ variant='solid'
91
92
  key={c.cmd}
92
93
  onClick={() => {
93
94
  copy(c.cmd)
@@ -35,7 +35,7 @@ export default class QuickCommandsFooter extends PureComponent {
35
35
  >
36
36
  <Button
37
37
  size='small'
38
- type='ghost'
38
+ type='text'
39
39
  >
40
40
  <span className='w500'>{e('quickCommands')}</span>
41
41
  <span className='l500'>Q</span>
@@ -736,7 +736,7 @@ export default class SessionWrapper extends Component {
736
736
  const layout = direction === 'leftRight' ? 'horizontal' : 'vertical'
737
737
  const [size1, size2] = this.state.splitSize
738
738
  const splitterProps = {
739
- layout,
739
+ orientation: layout,
740
740
  onResize: this.onSplitResize,
741
741
  onResizeEnd: this.onSplitResize,
742
742
  className: notSplitVew ? 'not-split-view' : '',
@@ -84,6 +84,7 @@ export default function DeepLinkControl () {
84
84
  return (
85
85
  <Tag
86
86
  key={protocol}
87
+ variant='solid'
87
88
  icon={isRegistered ? <CheckCircleOutlined /> : <CloseCircleOutlined />}
88
89
  color={isRegistered ? 'success' : 'default'}
89
90
  >
@@ -36,4 +36,4 @@
36
36
  .item-list
37
37
  .list-item-edit
38
38
  .list-item-apply
39
- right 20px
39
+ right 20px
@@ -600,6 +600,7 @@ export default class SettingCommon extends Component {
600
600
  <Tag
601
601
  color={main}
602
602
  className='mg1l'
603
+ variant='solid'
603
604
  style={
604
605
  {
605
606
  color: text
@@ -15,6 +15,7 @@ import TabQuickCommands from './tab-quick-commands'
15
15
  import TabSettings from './tab-settings'
16
16
  import TabThemes from './tab-themes'
17
17
  import TabProfiles from './tab-profiles'
18
+ import TabWidgets from './tab-widgets'
18
19
 
19
20
  const e = window.translate
20
21
 
@@ -92,6 +93,11 @@ export default auto(function SettingModalWrap (props) {
92
93
  key: settingMap.profiles,
93
94
  label: e(settingMap.profiles),
94
95
  children: null
96
+ },
97
+ {
98
+ key: settingMap.widgets,
99
+ label: <>{e(settingMap.widgets)} <sup>Beta</sup></>,
100
+ children: null
95
101
  }
96
102
  ]
97
103
  const tabsProps = {
@@ -141,6 +147,13 @@ export default auto(function SettingModalWrap (props) {
141
147
  store={store}
142
148
  settingTab={settingTab}
143
149
  />
150
+ <TabWidgets
151
+ listProps={props0}
152
+ settingItem={settingItem}
153
+ formProps={formProps}
154
+ store={store}
155
+ settingTab={settingTab}
156
+ />
144
157
  </>
145
158
  )
146
159
  }
@@ -21,7 +21,7 @@ export default class SettingWrap extends Component {
21
21
  open: this.props.visible,
22
22
  onClose: this.props.onCancel,
23
23
  className: 'setting-wrap',
24
- width: this.props.innerWidth - sidebarWidth,
24
+ size: this.props.innerWidth - sidebarWidth,
25
25
  zIndex: 888,
26
26
  placement: 'left',
27
27
  destroyOnHidden: true,
@@ -64,9 +64,9 @@ export default class StartSessionSelect extends PureComponent {
64
64
  ...(d.bookmarkIds || []).map(buildLeaf)
65
65
  ].filter(d => d)
66
66
  }
67
- if (!r.children.length) {
68
- return ''
69
- }
67
+ // if (!r.children.length) {
68
+ // return ''
69
+ // }
70
70
  return r
71
71
  }).filter(d => d)
72
72
  return level1
@@ -0,0 +1,35 @@
1
+ import SettingCol from './col'
2
+ import WidgetControl from '../widgets/widget-control'
3
+ import WidgetList from '../widgets/widgets-list'
4
+ import {
5
+ settingMap
6
+ } from '../../common/constants'
7
+
8
+ export default function TabWidgets (props) {
9
+ const {
10
+ settingTab
11
+ } = props
12
+ if (settingTab !== settingMap.widgets) {
13
+ return null
14
+ }
15
+ const {
16
+ settingItem,
17
+ listProps,
18
+ formProps
19
+ } = props
20
+ return (
21
+ <div
22
+ className='setting-tabs-profile'
23
+ >
24
+ <SettingCol>
25
+ <WidgetList
26
+ {...listProps}
27
+ />
28
+ <WidgetControl
29
+ {...formProps}
30
+ key={settingItem.id}
31
+ />
32
+ </SettingCol>
33
+ </div>
34
+ )
35
+ }
@@ -54,7 +54,7 @@ export default class AddrBookmarkItem extends Component {
54
54
  } = this.props
55
55
  const id = `${item.host}#${item.id}`
56
56
  const globTag = item.isGlobal
57
- ? <Tag color='green'>G</Tag>
57
+ ? <Tag color='green' variant='solid'>G</Tag>
58
58
  : null
59
59
  return (
60
60
  <div
@@ -23,7 +23,7 @@ export default (perm, _onClick) => {
23
23
  Object.keys(permission).map(n => {
24
24
  const type = permission[n]
25
25
  ? 'primary'
26
- : 'ghost'
26
+ : 'dashed'
27
27
  return (
28
28
  <Button
29
29
  key={n + 'permi'}
@@ -6,7 +6,8 @@ import {
6
6
  PlusCircleOutlined,
7
7
  SettingOutlined,
8
8
  UpCircleOutlined,
9
- BarsOutlined
9
+ BarsOutlined,
10
+ AppstoreOutlined
10
11
  } from '@ant-design/icons'
11
12
  import { Tooltip } from 'antd'
12
13
  import SideBarPanel from './sidebar-panel'
@@ -39,7 +40,8 @@ export default function Sidebar (props) {
39
40
  transferTab,
40
41
  showModal,
41
42
  showInfoModal,
42
- sidebarPanelTab
43
+ sidebarPanelTab,
44
+ openWidgetsModal
43
45
  } = props
44
46
 
45
47
  const { store } = window
@@ -100,6 +102,7 @@ export default function Sidebar (props) {
100
102
  const syncActive = showSetting && settingTab === settingMap.setting && settingItem.id === 'setting-sync'
101
103
  const themeActive = showSetting && settingTab === settingMap.terminalThemes
102
104
  const bookmarksActive = showSetting && settingTab === settingMap.bookmarks
105
+ const widgetsActive = showSetting && settingTab === settingMap.widgets
103
106
  const sideProps = openedSideBar
104
107
  ? {
105
108
  className: 'sidebar-list',
@@ -177,6 +180,13 @@ export default function Sidebar (props) {
177
180
  >
178
181
  <BarsOutlined className='iblock font20 control-icon' onClick={toggleBatchOp} />
179
182
  </SideIcon>
183
+ <SideIcon
184
+ title={e('widgets')}
185
+ active={widgetsActive}
186
+ >
187
+ <AppstoreOutlined className='iblock font20 control-icon' onClick={openWidgetsModal} />
188
+ </SideIcon>
189
+
180
190
  <SideIcon
181
191
  title={e('about')}
182
192
  active={showInfoModal}
@@ -48,7 +48,7 @@ export default memo(function TransferHistoryModal (props) {
48
48
  sorter: sorterFactory('typeFrom'),
49
49
  render: (type, inst) => {
50
50
  return (
51
- <Tag transfer={inst} />
51
+ <Tag transfer={inst} variant='solid' />
52
52
  )
53
53
  }
54
54
  }, {
@@ -120,9 +120,9 @@ export default class TerminalCmdSuggestions extends Component {
120
120
 
121
121
  // Use bottom position if close to bottom edge
122
122
  if (reverse) {
123
- position.bottom = h - top + cellHeight
123
+ position.bottom = h - top + cellHeight * 1.5
124
124
  } else {
125
- position.top = top
125
+ position.top = top + cellHeight
126
126
  }
127
127
  this.setState({
128
128
  showSuggestions: true,
@@ -7,6 +7,7 @@ import {
7
7
  CopyOutlined
8
8
  } from '@ant-design/icons'
9
9
  import { copy } from '../../common/clipboard'
10
+ import { escapeRegExp } from 'lodash-es'
10
11
 
11
12
  export default function SimpleEditor (props) {
12
13
  const [searchKeyword, setSearchKeyword] = useState('')
@@ -72,7 +73,8 @@ export default function SimpleEditor (props) {
72
73
 
73
74
  const matches = []
74
75
  const text = props.value || ''
75
- const regex = new RegExp(searchKeyword, 'gi')
76
+ const escapedKeyword = escapeRegExp(searchKeyword)
77
+ const regex = new RegExp(escapedKeyword, 'gi')
76
78
  let match
77
79
 
78
80
  while ((match = regex.exec(text)) !== null) {