@electerm/electerm-react 2.3.48 → 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.
@@ -17,18 +17,18 @@ function parsor (themeTxt) {
17
17
 
18
18
  const defaultThemeDark = () => {
19
19
  return parsor(`
20
- main = #20111b
21
- main-dark = #000
22
- main-light = #2E3338
23
- text = #ddd
24
- text-light = #fff
25
- text-dark = #888
26
- text-disabled = #777
27
- primary = #08c
28
- info = #FFD166
29
- success = #06D6A0
30
- error = #EF476F
31
- warn = #E55934
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
32
  `)
33
33
  }
34
34
  const defaultThemeLightFunc = () => {
@@ -50,27 +50,27 @@ const defaultThemeLightFunc = () => {
50
50
 
51
51
  const defaultThemeLightTerminal = () => {
52
52
  return parsor(`
53
- foreground=#333333
54
- background=#ededed
55
- cursor=#b5bd68
56
- cursorAccent=#1d1f21
57
- selectionBackground=rgba(0, 0, 0, 0.3)
58
- black=#575757
59
- red=#FF2C6D
60
- green=#19f9d8
61
- yellow=#FFB86C
62
- blue=#45A9F9
63
- magenta=#FF75B5
64
- cyan=#B084EB
65
- white=#CDCDCD
66
- brightBlack=#757575
67
- brightRed=#FF2C6D
68
- brightGreen=#19f9d8
69
- brightYellow=#FFCC95
70
- brightBlue=#6FC1FF
71
- brightMagenta=#FF9AC1
72
- brightCyan=#BCAAFE
73
- brightWhite=#E6E6E6
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
74
  `
75
75
  )
76
76
  }
@@ -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
@@ -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 />}
@@ -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)
@@ -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
  }
@@ -106,23 +106,18 @@ class Store {
106
106
  get inActiveTerminal () {
107
107
  const { store } = window
108
108
  if (store.showModal) {
109
- console.log('inActiveTerminal because showModal')
110
109
  return false
111
110
  }
112
111
  const { currentTab } = store
113
112
  if (!currentTab) {
114
- console.log('inActiveTerminal because no currentTab')
115
113
  return false
116
114
  }
117
115
  const {
118
116
  type
119
117
  } = currentTab
120
118
  if (type === 'web' || type === 'rdp' || type === 'vnc') {
121
- console.log('inActiveTerminal because type', type)
122
119
  return false
123
120
  }
124
- console.log('currentTab.sshSftpSplitView', currentTab.sshSftpSplitView)
125
- console.log('currentTab.pane', currentTab.pane)
126
121
  return currentTab.sshSftpSplitView ||
127
122
  currentTab.pane === paneMap.terminal
128
123
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "2.3.48",
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",