@electerm/electerm-react 2.3.36 → 2.3.58

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.
@@ -1,12 +1,9 @@
1
1
  /**
2
2
  * constants
3
3
  */
4
- import { getUiThemeConfig } from './ui-theme'
5
4
  import logoPath1Ref from '@electerm/electerm-resource/res/imgs/electerm-round-128x128.png'
6
5
  import logoPath2Ref from '@electerm/electerm-resource/res/imgs/electerm.png'
7
6
  import logoPath3Ref from '@electerm/electerm-resource/res/imgs/electerm-watermark.png'
8
- import dbDefaults from './db-defaults'
9
- import { get as _get } from 'lodash-es'
10
7
  export const packInfo = typeof window.et.packInfo === 'undefined' ? window.pre.packInfo : window.et.packInfo
11
8
  const buildConst = (props) => {
12
9
  return props.reduce((prev, key) => {
@@ -150,35 +147,6 @@ export const leftSidebarWidthKey = 'left-sidebar-width'
150
147
  export const rightSidebarWidthKey = 'right-sidebar-width'
151
148
  export const sftpDefaultSortSettingKey = 'sftp-default-sort'
152
149
  export const qmSortByFrequencyKey = 'qm-sort-by-frequency'
153
- // https://github.com/tinkertrain/panda-syntax-vscode/blob/master/themes/workbench.yaml
154
- export const defaultTheme = {
155
- id: 'default',
156
- name: 'default',
157
- themeConfig: {
158
- foreground: '#bbbbbb',
159
- background: '#141314',
160
- cursor: '#b5bd68',
161
- cursorAccent: '#1d1f21',
162
- selectionBackground: 'rgba(255, 255, 255, 0.3)',
163
- black: '#575757',
164
- red: '#FF2C6D',
165
- green: '#19f9d8',
166
- yellow: '#FFB86C',
167
- blue: '#45A9F9',
168
- magenta: '#FF75B5',
169
- cyan: '#B084EB',
170
- white: '#CDCDCD',
171
- brightBlack: '#757575',
172
- brightRed: '#FF2C6D',
173
- brightGreen: '#19f9d8',
174
- brightYellow: '#FFCC95',
175
- brightBlue: '#6FC1FF',
176
- brightMagenta: '#FF9AC1',
177
- brightCyan: '#BCAAFE',
178
- brightWhite: '#E6E6E6'
179
- },
180
- uiThemeConfig: getUiThemeConfig()
181
- }
182
150
 
183
151
  export const commonBaudRates = [
184
152
  110,
@@ -230,11 +198,6 @@ export const settingShortcutsId = 'setting-shortcuts'
230
198
  export const settingAiId = 'setting-ai'
231
199
  export const settingCommonId = 'setting-common'
232
200
  export const defaultEnvLang = 'en_US.UTF-8'
233
- const defaultThemeLightConf = _get(
234
- dbDefaults, '[0].data[1]'
235
- )
236
- defaultThemeLightConf.id = defaultThemeLightConf._id
237
- export const defaultThemeLight = defaultThemeLightConf
238
201
  export const fileActions = {
239
202
  cancel: 'cancel',
240
203
  skip: 'skip',
@@ -2,9 +2,12 @@
2
2
  * theme control
3
3
  */
4
4
 
5
- import { defaultTheme, settingMap } from '../common/constants'
5
+ import { settingMap } from '../common/constants'
6
6
  import download from '../common/download'
7
- import copy from 'json-deep-copy'
7
+ import {
8
+ defaultTheme
9
+ } from './theme-defaults'
10
+
8
11
  const e = window.translate
9
12
  const terminalPrefix = 'terminal:'
10
13
  export const requiredThemeProps = [
@@ -46,21 +49,13 @@ export const validThemeProps = [
46
49
  ...requiredThemeProps,
47
50
  'name'
48
51
  ]
49
- /**
50
- * build default themes
51
- */
52
- export const buildDefaultThemes = () => {
53
- return {
54
- [defaultTheme.id]: defaultTheme
55
- }
56
- }
57
52
 
58
53
  /**
59
54
  * build new theme
60
55
  */
61
- export const buildNewTheme = (theme = defaultTheme) => {
56
+ export const buildNewTheme = (theme = defaultTheme()) => {
62
57
  return Object.assign(
63
- copy(theme),
58
+ theme,
64
59
  {
65
60
  id: '',
66
61
  name: e('newTheme')
@@ -132,7 +127,7 @@ export const convertTheme = (themeTxt) => {
132
127
  * @return {array} extra keys
133
128
  */
134
129
  export const verifyTheme = (themeConfig) => {
135
- const keysRight = Object.keys(defaultTheme.themeConfig)
130
+ const keysRight = Object.keys(defaultTheme().themeConfig)
136
131
  const keys = Object.keys(themeConfig)
137
132
  const extraKeys = keys.filter(k => !keysRight.includes(k))
138
133
  return extraKeys
@@ -0,0 +1,120 @@
1
+ /**
2
+ * database default should init
3
+ */
4
+
5
+ function parsor (themeTxt) {
6
+ return themeTxt.split('\n').reduce((prev, line) => {
7
+ let [key = '', value = ''] = line.split('=')
8
+ key = key.trim()
9
+ value = value.trim()
10
+ if (!key || !value) {
11
+ return prev
12
+ }
13
+ prev[key] = value
14
+ return prev
15
+ }, {})
16
+ }
17
+
18
+ const defaultThemeDark = () => {
19
+ return parsor(`
20
+ main-dark=#000
21
+ main-light=#2E3338
22
+ text=#ddd
23
+ text-light=#fff
24
+ text-dark=#888
25
+ text-disabled=#777
26
+ primary=#08c
27
+ info=#FFD166
28
+ success=#06D6A0
29
+ error=#EF476F
30
+ warn=#E55934
31
+ main=#121214
32
+ `)
33
+ }
34
+ const defaultThemeLightFunc = () => {
35
+ return parsor(`
36
+ main=#ededed
37
+ main-dark=#cccccc
38
+ main-light=#fefefe
39
+ text=#555
40
+ text-light=#777
41
+ text-dark=#444
42
+ text-disabled=#888
43
+ primary=#08c
44
+ info=#FFD166
45
+ success=#06D6A0
46
+ error=#EF476F
47
+ warn=#E55934
48
+ `)
49
+ }
50
+
51
+ const defaultThemeLightTerminal = () => {
52
+ return parsor(`
53
+ background=#121214
54
+ foreground=#af9a91
55
+ cursor=#af9a91
56
+ selectionBackground=#575256
57
+ cursorAccent=#121214
58
+ black=#572100
59
+ red=#ba3934
60
+ green=#91773f
61
+ yellow=#b55600
62
+ blue=#5f63b4
63
+ magenta=#a17c7b
64
+ cyan=#8faea9
65
+ white=#af9a91
66
+ brightBlack=#4e4b61
67
+ brightRed=#d9443f
68
+ brightGreen=#d6b04e
69
+ brightYellow=#f66813
70
+ brightBlue=#8086ef
71
+ brightMagenta=#e2c2bb
72
+ brightCyan=#a4dce7
73
+ brightWhite=#d2c7a9
74
+ `
75
+ )
76
+ }
77
+
78
+ const defaultThemeDarkTerminal = () => {
79
+ return {
80
+ foreground: '#bbbbbb',
81
+ background: '#20111b',
82
+ cursor: '#b5bd68',
83
+ cursorAccent: '#1d1f21',
84
+ selectionBackground: 'rgba(255, 255, 255, 0.3)',
85
+ black: '#575757',
86
+ red: '#FF2C6D',
87
+ green: '#19f9d8',
88
+ yellow: '#FFB86C',
89
+ blue: '#45A9F9',
90
+ magenta: '#FF75B5',
91
+ cyan: '#B084EB',
92
+ white: '#CDCDCD',
93
+ brightBlack: '#757575',
94
+ brightRed: '#FF2C6D',
95
+ brightGreen: '#19f9d8',
96
+ brightYellow: '#FFCC95',
97
+ brightBlue: '#6FC1FF',
98
+ brightMagenta: '#FF9AC1',
99
+ brightCyan: '#BCAAFE',
100
+ brightWhite: '#E6E6E6'
101
+ }
102
+ }
103
+
104
+ export function defaultTheme () {
105
+ return {
106
+ id: 'default',
107
+ name: 'default',
108
+ themeConfig: defaultThemeDarkTerminal(),
109
+ uiThemeConfig: defaultThemeDark()
110
+ }
111
+ }
112
+
113
+ export function defaultThemeLight () {
114
+ return {
115
+ id: 'defaultLight',
116
+ name: 'default light',
117
+ themeConfig: defaultThemeLightTerminal(),
118
+ uiThemeConfig: defaultThemeLightFunc()
119
+ }
120
+ }
@@ -2,6 +2,7 @@
2
2
  window.refs = new Map()
3
3
  window.refsStatic = new Map()
4
4
  window.refsTransfers = new Map()
5
+ window.refsTabs = new Map()
5
6
 
6
7
  class Ref {
7
8
  constructor (key) {
@@ -21,6 +22,40 @@ class Ref {
21
22
  }
22
23
  }
23
24
 
25
+ class TabsRef extends Ref {
26
+ constructor (key) {
27
+ super(key)
28
+ // Map to track add count for each key: key -> number
29
+ this.addCounts = new Map()
30
+ }
31
+
32
+ add (key, inst) {
33
+ // Increment add count
34
+ const currentCount = this.addCounts.get(key) || 0
35
+ this.addCounts.set(key, currentCount + 1)
36
+
37
+ // Add/update the ref
38
+ window[this.key].set(key, inst)
39
+ }
40
+
41
+ remove (key) {
42
+ const currentCount = this.addCounts.get(key) || 0
43
+
44
+ if (currentCount <= 0) {
45
+ return
46
+ }
47
+
48
+ const newCount = currentCount - 1
49
+ if (newCount === 0) {
50
+ window[this.key].delete(key)
51
+ this.addCounts.delete(key)
52
+ } else {
53
+ this.addCounts.set(key, newCount)
54
+ }
55
+ }
56
+ }
57
+
24
58
  export const refs = new Ref('refs')
25
59
  export const refsTransfers = new Ref('refsTransfers')
26
60
  export const refsStatic = new Ref('refsStatic')
61
+ export const refsTabs = new TabsRef('refsTabs')
@@ -33,10 +33,15 @@
33
33
  display flex
34
34
  padding 8px 5px
35
35
  &:hover
36
- background var(--primary)
37
- color var(--primary-contrast)
38
- .sftp-transport .transfer-control-icon:hover
39
- color var(--primary)
36
+ font-weight bold
37
+ .transfer-control-icon
38
+ padding-right 5px
39
+ padding-left 5px
40
+ margin-top -2px
41
+ &:hover
42
+ color var(--primary-contrast)
43
+ background var(--primary)
44
+ border-radius 20px
40
45
 
41
46
  .flex-child + .flex-child
42
47
  margin-left 5px
@@ -8,7 +8,6 @@ import {
8
8
  UpCircleOutlined,
9
9
  BarsOutlined
10
10
  } from '@ant-design/icons'
11
- import { useRef, memo } from 'react'
12
11
  import { Tooltip } from 'antd'
13
12
  import SideBarPanel from './sidebar-panel'
14
13
  import TransferList from './transfer-list'
@@ -25,9 +24,7 @@ import './sidebar.styl'
25
24
 
26
25
  const e = window.translate
27
26
 
28
- export default memo(function Sidebar (props) {
29
- const handler = useRef(null)
30
-
27
+ export default function Sidebar (props) {
31
28
  const {
32
29
  height,
33
30
  upgradeInfo,
@@ -47,27 +44,35 @@ export default memo(function Sidebar (props) {
47
44
 
48
45
  const { store } = window
49
46
 
50
- const handleMouseLeave = () => {
51
- if (pinned) {
52
- return false
47
+ const handleClickOutside = (event) => {
48
+ // Don't close if pinned or has active input
49
+ if (store.pinned || hasActiveInput()) {
50
+ return
53
51
  }
54
52
 
55
- if (hasActiveInput()) {
56
- return false
53
+ // Check if click is outside the sidebar panel
54
+ const sidebarPanel = document.querySelector('.sidebar-panel')
55
+ if (sidebarPanel && !sidebarPanel.contains(event.target)) {
56
+ store.setOpenedSideBar('')
57
+ document.removeEventListener('click', handleClickOutside)
57
58
  }
58
-
59
- handler.current = setTimeout(
60
- () => store.setOpenedSideBar(''),
61
- 400
62
- )
63
59
  }
64
60
 
65
- const handleMouseEnterBookmark = () => {
61
+ const handleClickBookmark = () => {
66
62
  if (pinned) {
67
- return false
63
+ return
64
+ }
65
+ if (openedSideBar === 'bookmarks') {
66
+ // Remove listener when closing
67
+ document.removeEventListener('click', handleClickOutside)
68
+ store.setOpenedSideBar('')
69
+ } else {
70
+ // Add listener when opening, with slight delay to avoid conflict with this click
71
+ setTimeout(() => {
72
+ document.addEventListener('click', handleClickOutside)
73
+ }, 0)
74
+ store.setOpenedSideBar('bookmarks')
68
75
  }
69
- clearTimeout(handler.current)
70
- store.setOpenedSideBar('bookmarks')
71
76
  }
72
77
 
73
78
  const handleShowUpgrade = () => {
@@ -80,7 +85,6 @@ export default memo(function Sidebar (props) {
80
85
  openAbout,
81
86
  openSettingSync,
82
87
  openTerminalThemes,
83
- onClickBookmark,
84
88
  toggleBatchOp,
85
89
  setLeftSidePanelWidth
86
90
  } = store
@@ -137,9 +141,7 @@ export default memo(function Sidebar (props) {
137
141
  active={bookmarksActive}
138
142
  >
139
143
  <BookOutlined
140
- onMouseEnter={handleMouseEnterBookmark}
141
- onMouseLeave={handleMouseLeave}
142
- onClick={onClickBookmark}
144
+ onClick={handleClickBookmark}
143
145
  className='font20 iblock control-icon'
144
146
  />
145
147
  </SideIcon>
@@ -212,10 +214,8 @@ export default memo(function Sidebar (props) {
212
214
  <SideBarPanel
213
215
  pinned={pinned}
214
216
  sidebarPanelTab={sidebarPanelTab}
215
- onMouseEnter={handleMouseEnterBookmark}
216
- onMouseLeave={handleMouseLeave}
217
217
  />
218
218
  </SidePanel>
219
219
  </div>
220
220
  )
221
- })
221
+ }
@@ -5,9 +5,15 @@
5
5
  import { memo } from 'react'
6
6
  import BookmarkWrap from './bookmark'
7
7
  import History from './history'
8
- import { pick } from 'lodash-es'
9
8
  import { Tabs, Tooltip } from 'antd'
10
- import { ArrowsAltOutlined, EditOutlined, PlusCircleOutlined, ShrinkOutlined, PushpinOutlined } from '@ant-design/icons'
9
+ import {
10
+ ArrowsAltOutlined,
11
+ EditOutlined,
12
+ PlusCircleOutlined,
13
+ ShrinkOutlined,
14
+ PushpinOutlined,
15
+ UnorderedListOutlined
16
+ } from '@ant-design/icons'
11
17
 
12
18
  const e = window.translate
13
19
 
@@ -20,9 +26,21 @@ export default memo(function SidebarPanel (props) {
20
26
  const prps1 = {
21
27
  className: prps.className + (pinned ? ' pinned' : '')
22
28
  }
29
+ const props2 = {
30
+ onClick: store.clearHistory,
31
+ className: 'mg2x pointer clear-ai-icon icon-hover'
32
+ }
33
+ const tabBarExtraContent = sidebarPanelTab === 'history'
34
+ ? (
35
+ <UnorderedListOutlined
36
+ {...props2}
37
+ />
38
+ )
39
+ : null
23
40
  const tabsProps = {
24
41
  activeKey: sidebarPanelTab,
25
42
  onChange: store.handleSidebarPanelTab,
43
+ tabBarExtraContent,
26
44
  items: [
27
45
  {
28
46
  key: 'bookmarks',
@@ -69,7 +87,6 @@ export default memo(function SidebarPanel (props) {
69
87
  return (
70
88
  <div
71
89
  className='sidebar-panel bookmarks-panel animate-fast'
72
- {...pick(props, ['onMouseEnter', 'onMouseLeave'])}
73
90
  >
74
91
  <div className='sidebar-pin-top'>
75
92
  <div className='pd1y pd2t pd2x sidebar-panel-control alignright'>
@@ -27,7 +27,6 @@ export default class AddBtn extends Component {
27
27
  }
28
28
  this.addBtnRef = React.createRef()
29
29
  this.menuRef = React.createRef()
30
- this.hideTimeout = null
31
30
  this.portalContainer = null
32
31
  }
33
32
 
@@ -40,19 +39,9 @@ export default class AddBtn extends Component {
40
39
  return this.portalContainer
41
40
  }
42
41
 
43
- componentDidMount () {
44
- document.addEventListener('click', this.handleDocumentClick)
45
- // Listen for dropdown events to prevent menu closing
46
- document.addEventListener('ant-dropdown-show', this.handleDropdownShow)
47
- document.addEventListener('ant-dropdown-hide', this.handleDropdownHide)
48
- }
49
-
50
42
  componentWillUnmount () {
51
- document.removeEventListener('click', this.handleDocumentClick)
52
- document.removeEventListener('ant-dropdown-show', this.handleDropdownShow)
53
- document.removeEventListener('ant-dropdown-hide', this.handleDropdownHide)
54
- if (this.hideTimeout) {
55
- clearTimeout(this.hideTimeout)
43
+ if (this.state.open) {
44
+ document.removeEventListener('click', this.handleDocumentClick)
56
45
  }
57
46
  // Clean up portal container
58
47
  if (this.portalContainer) {
@@ -61,19 +50,15 @@ export default class AddBtn extends Component {
61
50
  }
62
51
  }
63
52
 
64
- handleDropdownShow = () => {
65
- // Cancel any pending hide timeout when dropdown shows
66
- if (this.hideTimeout) {
67
- clearTimeout(this.hideTimeout)
68
- this.hideTimeout = null
53
+ componentDidUpdate (prevProps, prevState) {
54
+ // Attach or detach document click listener only when menu open state changes
55
+ if (this.state.open && !prevState.open) {
56
+ document.addEventListener('click', this.handleDocumentClick)
57
+ } else if (!this.state.open && prevState.open) {
58
+ document.removeEventListener('click', this.handleDocumentClick)
69
59
  }
70
60
  }
71
61
 
72
- handleDropdownHide = () => {
73
- // Small delay after dropdown hides before allowing menu to close
74
- // This prevents flicker when dropdown closes
75
- }
76
-
77
62
  handleDocumentClick = (e) => {
78
63
  // Don't close menu when clicking inside menu or add button
79
64
  if (this.menuRef.current && this.menuRef.current.contains(e.target)) {
@@ -91,86 +76,6 @@ export default class AddBtn extends Component {
91
76
  this.setState({ open: false })
92
77
  }
93
78
 
94
- handleMouseEnter = () => {
95
- if (this.hideTimeout) {
96
- clearTimeout(this.hideTimeout)
97
- this.hideTimeout = null
98
- }
99
-
100
- // Calculate menu position
101
- if (this.addBtnRef.current) {
102
- const rect = this.addBtnRef.current.getBoundingClientRect()
103
- const windowWidth = window.innerWidth
104
- const windowHeight = window.innerHeight
105
-
106
- // Estimate menu width and height
107
- const estimatedMenuWidth = Math.min(300, windowWidth - 40) // Responsive width
108
- const estimatedMenuHeight = 400 // Rough estimate
109
-
110
- // Calculate fixed position coordinates
111
- let menuTop = rect.bottom + 4 // 4px margin
112
- let menuLeft = rect.left
113
- let menuPosition = 'right'
114
-
115
- // Check if menu would overflow bottom of screen
116
- if (menuTop + estimatedMenuHeight > windowHeight - 20) {
117
- menuTop = rect.top - estimatedMenuHeight - 4 // Show above button
118
- }
119
-
120
- // If aligning right would cause overflow, align left instead
121
- if (rect.left + estimatedMenuWidth > windowWidth - 20) {
122
- menuPosition = 'left'
123
- menuLeft = rect.right - estimatedMenuWidth
124
- }
125
-
126
- // If aligning left would cause overflow (negative position), force right
127
- if (menuLeft < 20) {
128
- menuPosition = 'right'
129
- menuLeft = Math.max(20, rect.left) // Ensure at least 20px from edge
130
- }
131
-
132
- // Final check to ensure menu doesn't go off screen
133
- if (menuLeft + estimatedMenuWidth > windowWidth - 20) {
134
- menuLeft = windowWidth - estimatedMenuWidth - 20
135
- }
136
-
137
- this.setState({
138
- open: true,
139
- menuPosition,
140
- menuTop,
141
- menuLeft
142
- })
143
-
144
- if (!this.state.open) {
145
- window.openTabBatch = this.props.batch
146
- }
147
- }
148
- }
149
-
150
- handleMouseLeave = () => {
151
- this.hideTimeout = setTimeout(() => {
152
- this.setState({ open: false })
153
- }, 200)
154
- }
155
-
156
- handleMenuMouseEnter = () => {
157
- if (this.hideTimeout) {
158
- clearTimeout(this.hideTimeout)
159
- this.hideTimeout = null
160
- }
161
- }
162
-
163
- handleMenuMouseLeave = () => {
164
- // Don't close if there's an active input or dropdown
165
- if (hasActiveInput()) {
166
- return
167
- }
168
-
169
- this.hideTimeout = setTimeout(() => {
170
- this.setState({ open: false })
171
- }, 200)
172
- }
173
-
174
79
  handleMenuScroll = (e) => {
175
80
  // Prevent scroll events from bubbling up
176
81
  e.stopPropagation()
@@ -212,8 +117,6 @@ export default class AddBtn extends Component {
212
117
  top: menuTop,
213
118
  left: menuLeft
214
119
  }}
215
- onMouseEnter={this.handleMenuMouseEnter}
216
- onMouseLeave={this.handleMenuMouseLeave}
217
120
  onScroll={this.handleMenuScroll}
218
121
  >
219
122
  <div
@@ -230,6 +133,59 @@ export default class AddBtn extends Component {
230
133
  )
231
134
  }
232
135
 
136
+ handleAddBtnClick = () => {
137
+ if (this.state.open) {
138
+ this.setState({ open: false })
139
+ } else {
140
+ // Calculate menu position and open the menu
141
+ if (this.addBtnRef.current) {
142
+ const rect = this.addBtnRef.current.getBoundingClientRect()
143
+ const windowWidth = window.innerWidth
144
+ const windowHeight = window.innerHeight
145
+
146
+ // Estimate menu width and height
147
+ const estimatedMenuWidth = Math.min(300, windowWidth - 40) // Responsive width
148
+ const estimatedMenuHeight = 400 // Rough estimate
149
+
150
+ // Calculate fixed position coordinates
151
+ let menuTop = rect.bottom + 4 // 4px margin
152
+ let menuLeft = rect.left
153
+ let menuPosition = 'right'
154
+
155
+ // Check if menu would overflow bottom of screen
156
+ if (menuTop + estimatedMenuHeight > windowHeight - 20) {
157
+ menuTop = rect.top - estimatedMenuHeight - 4 // Show above button
158
+ }
159
+
160
+ // If aligning right would cause overflow, align left instead
161
+ if (rect.left + estimatedMenuWidth > windowWidth - 20) {
162
+ menuPosition = 'left'
163
+ menuLeft = rect.right - estimatedMenuWidth
164
+ }
165
+
166
+ // If aligning left would cause overflow (negative position), force right
167
+ if (menuLeft < 20) {
168
+ menuPosition = 'right'
169
+ menuLeft = Math.max(20, rect.left) // Ensure at least 20px from edge
170
+ }
171
+
172
+ // Final check to ensure menu doesn't go off screen
173
+ if (menuLeft + estimatedMenuWidth > windowWidth - 20) {
174
+ menuLeft = windowWidth - estimatedMenuWidth - 20
175
+ }
176
+
177
+ this.setState({
178
+ open: true,
179
+ menuPosition,
180
+ menuTop,
181
+ menuLeft
182
+ })
183
+
184
+ window.openTabBatch = this.props.batch
185
+ }
186
+ }
187
+ }
188
+
233
189
  render () {
234
190
  const { empty, className } = this.props
235
191
  const { open } = this.state
@@ -246,9 +202,7 @@ export default class AddBtn extends Component {
246
202
  <PlusOutlined
247
203
  title={e('openNewTerm')}
248
204
  className={cls}
249
- onClick={this.handleTabAdd}
250
- onMouseEnter={this.handleMouseEnter}
251
- onMouseLeave={this.handleMouseLeave}
205
+ onClick={this.handleAddBtnClick}
252
206
  ref={this.addBtnRef}
253
207
  />
254
208
  {open && createPortal(this.renderMenus(), this.getPortalContainer())}
@@ -3,8 +3,9 @@
3
3
  position fixed
4
4
  z-index 1000
5
5
  background var(--main)
6
- border 1px solid var(--main-darker)
6
+ border 1px solid var(--main-lighter)
7
7
  border-radius 6px
8
+ box-shadow 0 4px 12px rgba(0, 0, 0, 0.3), 0 0 0 1px var(--main-darker)
8
9
  min-width 160px
9
10
  max-width 300px
10
11
  overflow-y auto
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { createRef } from 'react'
6
6
  import { Component } from 'manate/react/class-components'
7
- import { refs } from '../common/ref'
7
+ import { refsTabs } from '../common/ref'
8
8
  import {
9
9
  CloseOutlined,
10
10
  Loading3QuartersOutlined,
@@ -36,22 +36,13 @@ class Tab extends Component {
36
36
  terminalOnData: false
37
37
  }
38
38
  this.id = 'tab-' + this.props.tab.id
39
- refs.add(this.id, this)
39
+ refsTabs.add(this.id, this)
40
40
  }
41
41
 
42
42
  tabRef = createRef()
43
43
 
44
- componentDidUpdate (prevProps) {
45
- if (this.props.openContextMenu && !prevProps.openContextMenu) {
46
- this.handleContextMenu()
47
- }
48
- if (this.props.contextFunc && !prevProps.contextFunc) {
49
- this[this.props.contextFunc](...this.props.contextArgs)
50
- }
51
- }
52
-
53
44
  componentWillUnmount () {
54
- refs.remove(this.id)
45
+ refsTabs.remove(this.id)
55
46
  clearTimeout(this.timer)
56
47
  this.timer = null
57
48
  }
@@ -106,16 +106,17 @@
106
106
  .is-terminal-active .tab-terminal-feed
107
107
  display block
108
108
  animation blink 2s infinite
109
+ background-color transparent !important
109
110
  /* Remove opacity animation, use background-color */
110
111
  .tab-terminal-feed
111
112
  display none
112
113
  left 20px
113
114
  border-radius 0
114
115
  color var(--success)
115
- background none
116
116
  font-size 8px
117
117
  left 2px
118
118
  top 24px
119
+ background none
119
120
  .tab-close
120
121
  position absolute
121
122
  right 5px
@@ -131,8 +132,7 @@
131
132
  line-height 16px
132
133
  font-size 10px
133
134
  &:hover
134
- background var(--primary)
135
- color var(--primary-contast)
135
+ background var(--error)
136
136
 
137
137
  .tabs-add-btn
138
138
  display inline-block
@@ -84,6 +84,13 @@ export default function SimpleEditor (props) {
84
84
  e.preventDefault()
85
85
  }
86
86
  findMatches()
87
+ goToNextMatch()
88
+ }
89
+
90
+ function handleChange (e) {
91
+ setSearchKeyword(e.target.value)
92
+ findMatches()
93
+ goToNextMatch()
87
94
  }
88
95
 
89
96
  // Navigate to next match
@@ -144,7 +151,7 @@ export default function SimpleEditor (props) {
144
151
  <Flex className='mg1b' justify='space-between'>
145
152
  <Input.Search
146
153
  value={searchKeyword}
147
- onChange={e => setSearchKeyword(e.target.value)}
154
+ onChange={handleChange}
148
155
  placeholder='Search in text...'
149
156
  allowClear
150
157
  enterButton={<SearchOutlined />}
@@ -1,7 +1,13 @@
1
1
  import { useRef, useState } from 'react'
2
2
  import { Button, Input, message, Upload, Form, Space } from 'antd'
3
- import { convertTheme, convertThemeToText, exportTheme, validThemeProps, requiredThemeProps } from '../../common/terminal-theme'
4
- import { defaultTheme, defaultThemeLight } from '../../common/constants'
3
+ import {
4
+ convertTheme,
5
+ convertThemeToText,
6
+ exportTheme,
7
+ validThemeProps,
8
+ requiredThemeProps
9
+ } from '../../common/terminal-theme'
10
+ import { defaultTheme, defaultThemeLight } from '../../common/theme-defaults'
5
11
  import generate from '../../common/uid'
6
12
  import Link from '../common/external-link'
7
13
  import InputAutoFocus from '../common/input-auto-focus'
@@ -229,7 +235,7 @@ export default function ThemeForm (props) {
229
235
  themeName,
230
236
  themeText: convertThemeToText(props.formData)
231
237
  }
232
- const isDefaultTheme = id === defaultTheme.id || id === defaultThemeLight.id
238
+ const isDefaultTheme = id === defaultTheme().id || id === defaultThemeLight().id
233
239
  const disabled = readonly || isDefaultTheme
234
240
  const switchTxt = editor === 'theme-editor-txt' ? e('editWithColorPicker') : e('editWithTextEditor')
235
241
  const pickerProps = {
@@ -12,7 +12,7 @@ import {
12
12
  } from '@ant-design/icons'
13
13
  import { Tag, Tooltip, Button, Space } from 'antd'
14
14
  import classnames from 'classnames'
15
- import { defaultTheme } from '../../common/constants'
15
+ import { defaultTheme } from '../../common/theme-defaults'
16
16
  import highlight from '../common/highlight'
17
17
  import isColorDark from '../../common/is-color-dark'
18
18
 
@@ -138,7 +138,7 @@ export default function ThemeListItem (props) {
138
138
  active: activeItemId === id
139
139
  }
140
140
  )
141
- let title = id === defaultTheme.id
141
+ let title = id === defaultTheme().id
142
142
  ? e(id)
143
143
  : name
144
144
  title = highlight(
@@ -160,7 +160,7 @@ export default function ThemeListItem (props) {
160
160
  {renderTag()}{title}
161
161
  </div>
162
162
  {
163
- id === defaultTheme.id || type === 'iterm'
163
+ id === defaultTheme().id || type === 'iterm'
164
164
  ? null
165
165
  : props.renderDelBtn(item)
166
166
  }
@@ -7,7 +7,8 @@ import { LoadingOutlined, CheckCircleOutlined } from '@ant-design/icons'
7
7
  import { pick } from 'lodash-es'
8
8
  import { Pagination } from 'antd'
9
9
  import ThemeListItem from './theme-list-item'
10
- import { defaultTheme, settingMap } from '../../common/constants'
10
+ import { settingMap } from '../../common/constants'
11
+ import { defaultTheme } from '../../common/theme-defaults'
11
12
  import getInitItem from '../../common/init-setting-item'
12
13
  import './terminal-theme-list.styl'
13
14
 
@@ -48,7 +49,7 @@ export default class ThemeList extends List {
48
49
  return null
49
50
  }
50
51
  const { name, id } = item
51
- const title = id === defaultTheme.id
52
+ const title = id === defaultTheme().id
52
53
  ? e(id)
53
54
  : name
54
55
  return (
@@ -326,6 +326,10 @@ export default Store => {
326
326
  return aiConfigsArr.slice(0, -1).some(k => !window.store.config[k])
327
327
  }
328
328
 
329
+ Store.prototype.clearHistory = function () {
330
+ window.store.history = []
331
+ }
332
+
329
333
  Store.prototype.addCmdHistory = action(function (cmd) {
330
334
  const { terminalCommandHistory } = window.store
331
335
  terminalCommandHistory.add(cmd)
@@ -24,7 +24,6 @@ import {
24
24
  splitMap,
25
25
  cmdHistoryKey
26
26
  } from '../common/constants'
27
- import { buildDefaultThemes } from '../common/terminal-theme'
28
27
  import * as ls from '../common/safe-local-storage'
29
28
  import { exclude } from 'manate'
30
29
  import initSettingItem from '../common/init-setting-item'
@@ -61,9 +60,7 @@ export default () => {
61
60
  profiles: [],
62
61
  bookmarkGroups: getDefaultBookmarkGroups([]),
63
62
  _config: {},
64
- terminalThemes: [
65
- buildDefaultThemes()
66
- ],
63
+ terminalThemes: [],
67
64
  itermThemes: exclude([]),
68
65
  currentBookmarkGroupId: defaultBookmarkGroupId,
69
66
  expandedKeys: ls.getItemJSON(expandedKeysLsKey, [
@@ -181,7 +181,6 @@ export default (Store) => {
181
181
  })
182
182
  ext.lastDataUpdateTime = await getData('lastDataUpdateTime') || 0
183
183
  Object.assign(store, ext)
184
- store.checkDefaultTheme()
185
184
  store.loadFontList()
186
185
  store.fetchItermThemes()
187
186
  store.openInitSessions()
@@ -26,10 +26,6 @@ export default Store => {
26
26
  store.pinned = current
27
27
  }
28
28
 
29
- Store.prototype.onClickBookmark = function () {
30
- window.store.onNewSsh()
31
- }
32
-
33
29
  Store.prototype.handleSidebarPanelTab = function (tab) {
34
30
  window.store.sidebarPanelTab = tab
35
31
  }
@@ -40,7 +40,7 @@ import createTitle from '../common/create-title'
40
40
  import {
41
41
  theme
42
42
  } from 'antd'
43
- import { refs } from '../components/common/ref'
43
+ import { refsTabs } from '../components/common/ref'
44
44
 
45
45
  class Store {
46
46
  constructor () {
@@ -79,7 +79,7 @@ class Store {
79
79
  const {
80
80
  activeTabId
81
81
  } = this
82
- const tab = refs.get('tab-' + activeTabId)
82
+ const tab = refsTabs.get('tab-' + activeTabId)
83
83
  if (!tab) {
84
84
  return null
85
85
  }
@@ -9,7 +9,7 @@ import {
9
9
  paneMap,
10
10
  maxHistory
11
11
  } from '../common/constants'
12
- import { refs } from '../components/common/ref'
12
+ import { refs, refsTabs } from '../components/common/ref'
13
13
  import { message } from 'antd'
14
14
  import * as ls from '../common/safe-local-storage'
15
15
  import deepCopy from 'json-deep-copy'
@@ -571,7 +571,7 @@ export default Store => {
571
571
  })
572
572
 
573
573
  Store.prototype.notifyTabOnData = function (tabId) {
574
- const tab = refs.get('tab-' + tabId)
574
+ const tab = refsTabs.get('tab-' + tabId)
575
575
  if (tab) {
576
576
  tab.notifyOnData()
577
577
  }
@@ -2,21 +2,26 @@
2
2
  * theme related functions
3
3
  */
4
4
 
5
- import { message } from 'antd'
6
- import { isEqual } from 'lodash-es'
7
5
  import {
8
- defaultTheme,
9
- settingMap,
10
- defaultThemeLight
6
+ settingMap
11
7
  } from '../common/constants'
12
- import copy from 'json-deep-copy'
13
8
  import { convertTheme } from '../common/terminal-theme'
14
-
15
- const e = window.translate
9
+ import {
10
+ defaultTheme,
11
+ defaultThemeLight
12
+ } from '../common/theme-defaults'
16
13
 
17
14
  export default Store => {
18
15
  Store.prototype.getTerminalThemes = function () {
19
- return window.store.getItems(settingMap.terminalThemes)
16
+ const t1 = defaultTheme()
17
+ const t2 = defaultThemeLight()
18
+ return [
19
+ t1,
20
+ t2,
21
+ ...window.store.getItems(settingMap.terminalThemes).filter(d => {
22
+ return d && d.id !== t1.id && d.id !== t2.id
23
+ })
24
+ ]
20
25
  }
21
26
 
22
27
  Store.prototype.setTheme = function (id) {
@@ -47,14 +52,16 @@ export default Store => {
47
52
 
48
53
  Store.prototype.fixThemes = function (themes) {
49
54
  return themes.map(t => {
50
- const isDefaultTheme = t.id === defaultTheme.id
51
- const isDefaultThemeLight = t.id === defaultThemeLight.id
55
+ const d1 = defaultTheme()
56
+ const d2 = defaultThemeLight()
57
+ const isDefaultTheme = t.id === d1.id
58
+ const isDefaultThemeLight = t.id === d2.id
52
59
  if (isDefaultTheme) {
53
- Object.assign(t, defaultTheme)
60
+ Object.assign(t, d1)
54
61
  } else if (isDefaultThemeLight) {
55
- Object.assign(t, defaultThemeLight)
62
+ Object.assign(t, d2)
56
63
  } else if (!t.uiThemeConfig) {
57
- t.uiThemeConfig = copy(defaultTheme.uiThemeConfig)
64
+ t.uiThemeConfig = d1.uiThemeConfig
58
65
  }
59
66
  return t
60
67
  })
@@ -78,33 +85,4 @@ export default Store => {
78
85
  })
79
86
  )
80
87
  }
81
-
82
- Store.prototype.checkDefaultTheme = async function (terminalThemes) {
83
- const { store } = window
84
- const themeId = defaultTheme.id
85
- const currentDefaultTheme = store.terminalThemes.find(d => d.id === themeId)
86
- if (
87
- currentDefaultTheme &&
88
- (
89
- !isEqual(currentDefaultTheme.themeConfig, defaultTheme.themeConfig) || !isEqual(currentDefaultTheme.uiThemeConfig, defaultTheme.uiThemeConfig) ||
90
- currentDefaultTheme.name !== defaultTheme.name
91
- )
92
- ) {
93
- store.editTheme(
94
- themeId,
95
- {
96
- name: defaultTheme.name,
97
- themeConfig: defaultTheme.themeConfig,
98
- uiThemeConfig: defaultTheme.uiThemeConfig
99
- }
100
- )
101
- message.info(
102
- `${e('default')} ${e('themeConfig')} ${e('updated')}`
103
- )
104
- }
105
- const hasLightTheme = store.getTerminalThemes().find(d => d.id === defaultThemeLight.id)
106
- if (!hasLightTheme) {
107
- store.addTheme(defaultThemeLight)
108
- }
109
- }
110
88
  }
@@ -3,9 +3,12 @@
3
3
  */
4
4
 
5
5
  import {
6
- defaultTheme,
7
6
  settingMap
8
7
  } from '../common/constants'
8
+ import {
9
+ defaultTheme
10
+ } from '../common/theme-defaults'
11
+
9
12
  import copy from 'json-deep-copy'
10
13
 
11
14
  export default Store => {
@@ -15,6 +18,6 @@ export default Store => {
15
18
  .find(d => d.id === store.config.theme)
16
19
  return theme && theme.uiThemeConfig
17
20
  ? copy(theme.uiThemeConfig)
18
- : defaultTheme.uiThemeConfig
21
+ : defaultTheme().uiThemeConfig
19
22
  }
20
23
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "2.3.36",
3
+ "version": "2.3.58",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",
@@ -1,123 +0,0 @@
1
- /**
2
- * database default should init
3
- */
4
-
5
- function parsor (themeTxt) {
6
- return themeTxt.split('\n').reduce((prev, line) => {
7
- let [key = '', value = ''] = line.split('=')
8
- key = key.trim()
9
- value = value.trim()
10
- if (!key || !value) {
11
- return prev
12
- }
13
- prev[key] = value
14
- return prev
15
- }, {})
16
- }
17
-
18
- const defaultTheme = parsor(`
19
- main = #141314
20
- main-dark = #000
21
- main-light = #2E3338
22
- text = #ddd
23
- text-light = #fff
24
- text-dark = #888
25
- text-disabled = #777
26
- primary = #08c
27
- info = #FFD166
28
- success = #06D6A0
29
- error = #EF476F
30
- warn = #E55934
31
- `)
32
- const defaultThemeLight = parsor(`
33
- main=#ededed
34
- main-dark=#cccccc
35
- main-light=#fefefe
36
- text=#555
37
- text-light=#777
38
- text-dark=#444
39
- text-disabled=#888
40
- primary=#08c
41
- info=#FFD166
42
- success=#06D6A0
43
- error=#EF476F
44
- warn=#E55934
45
- `)
46
- const defaultThemeLightTerminal = parsor(`
47
- foreground=#333333
48
- background=#ededed
49
- cursor=#b5bd68
50
- cursorAccent=#1d1f21
51
- selectionBackground=rgba(0, 0, 0, 0.3)
52
- black=#575757
53
- red=#FF2C6D
54
- green=#19f9d8
55
- yellow=#FFB86C
56
- blue=#45A9F9
57
- magenta=#FF75B5
58
- cyan=#B084EB
59
- white=#CDCDCD
60
- brightBlack=#757575
61
- brightRed=#FF2C6D
62
- brightGreen=#19f9d8
63
- brightYellow=#FFCC95
64
- brightBlue=#6FC1FF
65
- brightMagenta=#FF9AC1
66
- brightCyan=#BCAAFE
67
- brightWhite=#E6E6E6
68
- `)
69
-
70
- const defaultThemeTerminal = {
71
- foreground: '#bbbbbb',
72
- background: '#141314',
73
- cursor: '#b5bd68',
74
- cursorAccent: '#1d1f21',
75
- selectionBackground: 'rgba(255, 255, 255, 0.3)',
76
- black: '#575757',
77
- red: '#FF2C6D',
78
- green: '#19f9d8',
79
- yellow: '#FFB86C',
80
- blue: '#45A9F9',
81
- magenta: '#FF75B5',
82
- cyan: '#B084EB',
83
- white: '#CDCDCD',
84
- brightBlack: '#757575',
85
- brightRed: '#FF2C6D',
86
- brightGreen: '#19f9d8',
87
- brightYellow: '#FFCC95',
88
- brightBlue: '#6FC1FF',
89
- brightMagenta: '#FF9AC1',
90
- brightCyan: '#BCAAFE',
91
- brightWhite: '#E6E6E6'
92
- }
93
-
94
- export default [
95
- {
96
- db: 'terminalThemes',
97
- data: [
98
- {
99
- _id: 'default',
100
- name: 'default',
101
- themeConfig: defaultThemeTerminal,
102
- uiThemeConfig: defaultTheme
103
- },
104
- {
105
- _id: 'defaultLight',
106
- name: 'default light',
107
- themeConfig: defaultThemeLightTerminal,
108
- uiThemeConfig: defaultThemeLight
109
- }
110
- ]
111
- },
112
- {
113
- db: 'bookmarkGroups',
114
- data: [
115
- {
116
- _id: 'default',
117
- title: 'default',
118
- bookmarkIds: [],
119
- bookmarkGroupIds: []
120
- }
121
- ]
122
- }
123
- ]