@electerm/electerm-react 1.50.65 → 1.51.0

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 (33) hide show
  1. package/client/common/constants.js +0 -13
  2. package/client/common/is-color-dark.js +33 -0
  3. package/client/components/footer/batch-input.jsx +3 -2
  4. package/client/components/layout/layout-item.jsx +1 -23
  5. package/client/components/layout/layout.jsx +55 -19
  6. package/client/components/layout/layouts.jsx +2 -10
  7. package/client/components/layout/pixed.js +9 -0
  8. package/client/components/main/css-overwrite.jsx +2 -2
  9. package/client/components/quick-commands/quick-commands-select.jsx +1 -1
  10. package/client/components/session/session.jsx +100 -22
  11. package/client/components/session/session.styl +9 -5
  12. package/client/components/session/sessions.jsx +45 -451
  13. package/client/components/setting-panel/setting-modal.jsx +2 -1
  14. package/client/components/sftp/sftp-entry.jsx +1 -1
  15. package/client/components/shortcuts/shortcut-control.jsx +18 -6
  16. package/client/components/sidebar/info-modal.jsx +8 -1
  17. package/client/components/sidebar/side-panel.jsx +1 -1
  18. package/client/components/tabs/index.jsx +70 -9
  19. package/client/components/tabs/on-tab-drop.js +25 -0
  20. package/client/components/tabs/tab.jsx +67 -119
  21. package/client/components/tabs/tabs.styl +12 -1
  22. package/client/components/terminal/index.jsx +14 -11
  23. package/client/components/terminal/terminal-interactive.jsx +1 -7
  24. package/client/components/terminal/terminal.styl +1 -2
  25. package/client/components/theme/theme-form.jsx +1 -1
  26. package/client/components/theme/theme-list-item.jsx +148 -0
  27. package/client/components/theme/theme-list.jsx +18 -72
  28. package/client/store/common.js +0 -7
  29. package/client/store/index.js +5 -39
  30. package/client/store/init-state.js +1 -1
  31. package/client/store/tab.js +338 -86
  32. package/client/store/watch.js +1 -6
  33. package/package.json +1 -1
@@ -43,7 +43,6 @@ export const maxSftpHistory = 20
43
43
  export const maxZoom = 8
44
44
  export const minZoom = 0.5
45
45
  export const extraTabWidth = 113
46
- // export const maxTabs = 20
47
46
 
48
47
  export const tabWidth = 160
49
48
 
@@ -252,18 +251,6 @@ export const fileActions = {
252
251
  renameAll: 'renameAll'
253
252
  }
254
253
 
255
- export const tabActions = {
256
- updateTabsStatus: 'update-tabs-status',
257
- setAllTabOffline: 'set-all-tab-offline',
258
- changeCurrentTabId: 'changeCurrentTabId',
259
- onDuplicateTab: 'on-duplicate-tab',
260
- reloadTab: 'reload-tab',
261
- initFirstTab: 'init-first-tab',
262
- delTab: 'del-tab',
263
- addTab: 'add-tab',
264
- updateTabs: 'update-tabs'
265
- }
266
-
267
254
  export const commonActions = {
268
255
  returnTermLogState: 'return-term-log-state',
269
256
  getTermLogState: 'get-term-log-state',
@@ -0,0 +1,33 @@
1
+ function expandShorthandColor (color) {
2
+ if (color.length === 4) {
3
+ return '#' + color[1] + color[1] + color[2] + color[2] + color[3] + color[3]
4
+ }
5
+ if (color.length === 7) {
6
+ return color
7
+ }
8
+ if (color.length < 7) {
9
+ return expandShorthandColor(color + 'f')
10
+ }
11
+ if (color.length > 7) {
12
+ return expandShorthandColor(color.slice(0, 7))
13
+ }
14
+ if (!/^#[A-Fa-f0-9]{6}$/.test(color)) {
15
+ return '#141314'
16
+ }
17
+ }
18
+
19
+ export default function isColorDark (_color) {
20
+ let color = expandShorthandColor(_color)
21
+ if (color.charAt(0) === '#') {
22
+ color = color.slice(1) // Remove the '#' if present
23
+ }
24
+ const r = parseInt(color.substr(0, 2), 16)
25
+ const g = parseInt(color.substr(2, 2), 16)
26
+ const b = parseInt(color.substr(4, 2), 16)
27
+
28
+ // Formula to determine brightness
29
+ const brightness = (r * 299 + g * 587 + b * 114) / 1000
30
+
31
+ // Decide based on brightness threshold
32
+ return brightness < 128 // You can adjust this threshold as needed
33
+ }
@@ -17,6 +17,7 @@ import {
17
17
  import TabSelect from './tab-select'
18
18
  import postMsg from '../../common/post-msg'
19
19
  import classNames from 'classnames'
20
+ import deepCopy from 'json-deep-copy'
20
21
 
21
22
  const e = window.translate
22
23
 
@@ -178,11 +179,11 @@ export default class BatchInput extends Component {
178
179
 
179
180
  getTabs = () => {
180
181
  const { currentTabId } = this.props
181
- return this.props.tabs.filter(tab => {
182
+ return deepCopy(this.props.tabs.filter(tab => {
182
183
  return tab.type !== terminalWebType &&
183
184
  tab.type !== terminalRdpType &&
184
185
  tab.type !== terminalVncType
185
- }).sort((a, b) => {
186
+ })).sort((a, b) => {
186
187
  // Current tab goes first
187
188
  if (a.id === currentTabId) return -1
188
189
  if (b.id === currentTabId) return 1
@@ -1,8 +1,3 @@
1
- import {
2
- tabActions
3
- } from '../../common/constants'
4
- import postMsg from '../../common/post-msg'
5
-
6
1
  export default function LayoutItem (props) {
7
2
  const {
8
3
  children,
@@ -10,16 +5,6 @@ export default function LayoutItem (props) {
10
5
  batch,
11
6
  ...itemProps
12
7
  } = props
13
- function handleClick (e) {
14
- let currentElement = e.target
15
- while (currentElement) {
16
- if (currentElement.classList && currentElement.classList.contains('tabs-dd-icon')) {
17
- return false
18
- }
19
- currentElement = currentElement.parentElement
20
- }
21
- window.store.currentLayoutBatch = i
22
- }
23
8
 
24
9
  function getDom () {
25
10
  return document.querySelector(`.layout-item.v${batch + 1}`)
@@ -38,9 +23,8 @@ export default function LayoutItem (props) {
38
23
  }
39
24
  currentElement = currentElement.parentElement
40
25
  }
41
- // debug('target drop', target)
42
26
  const fromTab = JSON.parse(e.dataTransfer.getData('fromFile'))
43
- const onDropElem = getDom
27
+ const onDropElem = getDom()
44
28
  if (!onDropElem || !fromTab || fromTab.batch === batch) {
45
29
  return
46
30
  }
@@ -51,11 +35,6 @@ export default function LayoutItem (props) {
51
35
  return
52
36
  }
53
37
  t.batch = batch
54
- postMsg({
55
- action: tabActions.changeCurrentTabId,
56
- currentTabId: store.currentTabId
57
- })
58
- store.setTabs(tabs)
59
38
  clearCls()
60
39
  }
61
40
 
@@ -82,7 +61,6 @@ export default function LayoutItem (props) {
82
61
  return (
83
62
  <div
84
63
  {...itemProps}
85
- onClick={handleClick}
86
64
  onDragEnter={onDragEnter}
87
65
  onDragLeave={onDragLeave}
88
66
  onDragEnd={onDragEnd}
@@ -1,6 +1,6 @@
1
1
  import { auto } from 'manate/react'
2
2
  import Layouts from './layouts'
3
- import Sessions from '../session/sessions'
3
+ import TabsWrap from '../tabs/index'
4
4
  import {
5
5
  splitConfig,
6
6
  quickCommandBoxHeight,
@@ -10,11 +10,20 @@ import layoutAlg from './layout-alg'
10
10
  import calcSessionSize from './session-size-alg'
11
11
  import TermSearch from '../terminal/term-search'
12
12
  import Footer from '../footer/footer-entry'
13
+ import SessionsWrap from '../session/sessions'
13
14
  import QuickCommandsFooterBox from '../quick-commands/quick-commands-box'
15
+ import pixed from './pixed'
16
+ import copy from 'json-deep-copy'
14
17
  import { pick } from 'lodash-es'
15
18
  import './layout.styl'
16
19
 
17
20
  export default auto(function Layout (props) {
21
+ const { store } = props
22
+ const {
23
+ layout, config, currentTab
24
+ } = store
25
+ const conf = splitConfig[layout]
26
+
18
27
  const handleMousedown = (e) => {
19
28
 
20
29
  }
@@ -61,12 +70,22 @@ export default auto(function Layout (props) {
61
70
  const h = height - footerHeight - (pinnedQuickCommandBar ? quickCommandBoxHeight : 0)
62
71
  return layoutAlg(layout, w, h)
63
72
  }
73
+ const layoutSize = calcLayoutStyle()
74
+ const {
75
+ width,
76
+ height
77
+ } = layoutSize
78
+ const pixedLayoutStyle = pixed(layoutSize)
79
+ const styles = buildLayoutStyles(conf, layout)
80
+ const layoutProps = {
81
+ layout,
82
+ ...styles,
83
+ layoutStyle: pixedLayoutStyle,
84
+ handleMousedown
85
+ }
86
+ const sizes = calcSessionSize(layout, width, height)
64
87
 
65
88
  function renderSessions (conf, layout) {
66
- const {
67
- width,
68
- height
69
- } = calcLayoutStyle()
70
89
  const {
71
90
  store
72
91
  } = props
@@ -79,10 +98,11 @@ export default auto(function Layout (props) {
79
98
  }
80
99
  tabsBatch[batch].push(tab)
81
100
  }
82
- return calcSessionSize(layout, width, height).map((v, i) => {
101
+ return sizes.map((v, i) => {
83
102
  const sessProps = {
84
103
  batch: i,
85
104
  layout,
105
+ currentBatchTabId: store[`currentTabId${i}`],
86
106
  ...v,
87
107
  tabs: tabsBatch[i] || [],
88
108
  ...pick(store, [
@@ -91,7 +111,6 @@ export default auto(function Layout (props) {
91
111
  'resolutions',
92
112
  'hideDelKeyTip',
93
113
  'fileOperation',
94
- 'file',
95
114
  'pinnedQuickCommandBar',
96
115
  'tabsHeight',
97
116
  'appPath',
@@ -101,7 +120,7 @@ export default auto(function Layout (props) {
101
120
  ])
102
121
  }
103
122
  return (
104
- <Sessions
123
+ <TabsWrap
105
124
  key={'sess' + i}
106
125
  {...sessProps}
107
126
  />
@@ -109,17 +128,6 @@ export default auto(function Layout (props) {
109
128
  })
110
129
  }
111
130
 
112
- const { store } = props
113
- const {
114
- layout, config, currentTab
115
- } = store
116
- const conf = splitConfig[layout]
117
- const layoutProps = {
118
- layout,
119
- ...buildLayoutStyles(conf, layout),
120
- layoutStyle: calcLayoutStyle(),
121
- handleMousedown
122
- }
123
131
  const termProps = {
124
132
  currentTab,
125
133
  config,
@@ -146,10 +154,38 @@ export default auto(function Layout (props) {
146
154
  'openedSideBar',
147
155
  'currentQuickCommands'
148
156
  ])
157
+ const sessionsProps = {
158
+ styles: styles.wrapStyles,
159
+ sizes,
160
+ width,
161
+ height,
162
+ layoutStyle: pixedLayoutStyle,
163
+ ...pick(store, [
164
+ 'currentTabId',
165
+ 'currentTabId0',
166
+ 'currentTabId1',
167
+ 'currentTabId2',
168
+ 'currentTabId3',
169
+ 'batch',
170
+ 'resolutions',
171
+ 'hideDelKeyTip',
172
+ 'fileOperation',
173
+ 'file',
174
+ 'pinnedQuickCommandBar',
175
+ 'tabsHeight',
176
+ 'appPath',
177
+ 'leftSidebarWidth',
178
+ 'pinned',
179
+ 'openedSideBar',
180
+ 'config'
181
+ ]),
182
+ tabs: copy(store.tabs)
183
+ }
149
184
  return [
150
185
  <Layouts {...layoutProps} key='layouts'>
151
186
  {renderSessions(conf, layout)}
152
187
  </Layouts>,
188
+ <SessionsWrap key='SessionsWrap' {...sessionsProps} />,
153
189
  <TermSearch
154
190
  key='TermSearch'
155
191
  {...termProps}
@@ -3,16 +3,8 @@ import {
3
3
  splitConfig
4
4
  } from '../../common/constants'
5
5
  import LayoutItem from './layout-item'
6
+ import pixed from './pixed'
6
7
 
7
- function pixed (style) {
8
- return Object.keys(style).reduce((prev, k) => {
9
- const v = style[k]
10
- return {
11
- ...prev,
12
- [k]: isNaN(v) ? v : v + 'px'
13
- }
14
- }, {})
15
- }
16
8
  export default memo(function LayoutWrap (props) {
17
9
  const {
18
10
  children,
@@ -28,7 +20,7 @@ export default memo(function LayoutWrap (props) {
28
20
  } = splitConfig[layout]
29
21
  const wrapStyle = {
30
22
  className: 'layout-wrap layout-wrap-' + layout,
31
- style: pixed(layoutStyle)
23
+ style: layoutStyle
32
24
  }
33
25
  return (
34
26
  <div {...wrapStyle}>
@@ -0,0 +1,9 @@
1
+ export default function pixed (style) {
2
+ return Object.keys(style).reduce((prev, k) => {
3
+ const v = style[k]
4
+ return {
5
+ ...prev,
6
+ [k]: isNaN(v) ? v : v + 'px'
7
+ }
8
+ }, {})
9
+ }
@@ -41,7 +41,7 @@ export default class CssOverwrite extends PureComponent {
41
41
  st = `url(${terminalBackgroundImagePath}) !important`
42
42
  }
43
43
  if (!st) {
44
- return `#container .session-current .xterm-viewport {
44
+ return `#container .session-batch-active .xterm-screen {
45
45
  background-image: url("./images/electerm-watermark.png");
46
46
  }`
47
47
  }
@@ -65,7 +65,7 @@ export default class CssOverwrite extends PureComponent {
65
65
  })`)
66
66
  }
67
67
 
68
- return `#container .session-current .xterm-viewport {
68
+ return `#container .session-batch-active .xterm-screen {
69
69
  ${styles.join(';')}
70
70
  }`
71
71
  }
@@ -29,7 +29,7 @@ export default class QuickCommandsFooter extends PureComponent {
29
29
  render () {
30
30
  return (
31
31
  <div
32
- className='fleft relative'
32
+ className='fleft relative quick-command-trigger-wrap'
33
33
  onMouseEnter={this.handleOpen}
34
34
  onMouseLeave={this.handleMouseLeave}
35
35
  >
@@ -6,6 +6,7 @@ import Term from '../terminal'
6
6
  import Sftp from '../sftp/sftp-entry'
7
7
  import RdpSession from '../rdp/rdp-session'
8
8
  import VncSession from '../vnc/vnc-session'
9
+ import WebSession from '../web/web-session.jsx'
9
10
  import {
10
11
  SearchOutlined,
11
12
  FullscreenOutlined,
@@ -24,7 +25,8 @@ import {
24
25
  terminalActions,
25
26
  connectionMap,
26
27
  terminalRdpType,
27
- terminalVncType
28
+ terminalVncType,
29
+ terminalWebType
28
30
  } from '../../common/constants'
29
31
  import safeName from '../../common/safe-name'
30
32
  import postMessage from '../../common/post-msg'
@@ -55,6 +57,53 @@ export default class SessionWrapper extends Component {
55
57
  clearTimeout(this.backspaceKeyPressedTimer)
56
58
  }
57
59
 
60
+ getDom = () => {
61
+ return document.getElementById(`is-${this.props.tab.id}`)
62
+ }
63
+
64
+ onDrop = (e) => {
65
+ e.preventDefault()
66
+ const { target } = e
67
+ if (!target) {
68
+ return
69
+ }
70
+ const fromTab = JSON.parse(e.dataTransfer.getData('fromFile'))
71
+ const onDropElem = this.getDom()
72
+ const { batch } = this.props.tab
73
+ if (!onDropElem || !fromTab || fromTab.batch === batch) {
74
+ return
75
+ }
76
+ const { store } = window
77
+ const { tabs } = store
78
+ const t = tabs.find(t => t.id === fromTab.id)
79
+ if (!t) {
80
+ return
81
+ }
82
+ t.batch = batch
83
+ this.clearCls()
84
+ }
85
+
86
+ clearCls = () => {
87
+ this.getDom()?.classList.remove('drag-over')
88
+ }
89
+
90
+ addCls = () => {
91
+ this.getDom()?.classList.add('drag-over')
92
+ }
93
+
94
+ onDragEnter = () => {
95
+ this.addCls()
96
+ }
97
+
98
+ onDragLeave = (e) => {
99
+ this.clearCls()
100
+ }
101
+
102
+ onDragEnd = (e) => {
103
+ this.clearCls()
104
+ e && e.dataTransfer && e.dataTransfer.clearData()
105
+ }
106
+
58
107
  onDelKeyPressed = () => {
59
108
  this.setState({
60
109
  delKeyPressed: true
@@ -120,11 +169,7 @@ export default class SessionWrapper extends Component {
120
169
  }
121
170
 
122
171
  computePosition = (index) => {
123
- const windowWidth = this.getWidth()
124
- const heightAll = this.props.computeHeight()
125
172
  return {
126
- height: heightAll,
127
- width: windowWidth,
128
173
  left: 0,
129
174
  top: 0
130
175
  }
@@ -144,9 +189,26 @@ export default class SessionWrapper extends Component {
144
189
  sessionId,
145
190
  sftpPathFollowSsh
146
191
  } = this.state
192
+ const {
193
+ tab
194
+ } = this.props
147
195
  const {
148
196
  pane, type
149
- } = this.props.tab
197
+ } = tab
198
+ if (type === terminalWebType) {
199
+ const webProps = {
200
+ tab,
201
+ width: this.props.width,
202
+ height: this.props.height,
203
+ reloadTab: this.props.reloadTab,
204
+ currentBatchTabId: this.props.currentBatchTabId
205
+ }
206
+ return (
207
+ <WebSession
208
+ {...webProps}
209
+ />
210
+ )
211
+ }
150
212
  if (type === terminalRdpType || type === terminalVncType) {
151
213
  const rdpProps = {
152
214
  tab: this.props.tab,
@@ -186,9 +248,10 @@ export default class SessionWrapper extends Component {
186
248
  const cls = pane === paneMap.terminal
187
249
  ? 'terms-box'
188
250
  : 'terms-box hide'
189
- const height = this.props.computeHeight()
190
- const { tab } = this.props
191
251
  const width = this.getWidth()
252
+ const height = this.props.computeHeight(
253
+ this.props.height
254
+ )
192
255
  const themeConfig = copy(window.store.getThemeConfig())
193
256
  const logName = safeName(`${tab.title ? tab.title + '_' : ''}${tab.host ? tab.host + '_' : ''}${tab.id}`)
194
257
  const pops = {
@@ -203,7 +266,9 @@ export default class SessionWrapper extends Component {
203
266
  'setCwd',
204
267
  'onDelKeyPressed'
205
268
  ]),
206
- ...this.computePosition()
269
+ ...this.computePosition(),
270
+ width,
271
+ height
207
272
  }
208
273
  return (
209
274
  <div
@@ -214,7 +279,6 @@ export default class SessionWrapper extends Component {
214
279
  }}
215
280
  >
216
281
  <Term
217
- key={tab.id}
218
282
  logName={logName}
219
283
  sessionId={sessionId}
220
284
  sessionOptions={sessionOptions}
@@ -233,10 +297,16 @@ export default class SessionWrapper extends Component {
233
297
  cwd
234
298
  } = this.state
235
299
  const { pane, type, id } = this.props.tab
236
- if (type === terminalRdpType) {
300
+ if (
301
+ type === terminalRdpType ||
302
+ type === terminalVncType ||
303
+ type === terminalWebType
304
+ ) {
237
305
  return null
238
306
  }
239
- const height = this.props.computeHeight(pane)
307
+ const height = this.props.computeHeight(
308
+ this.props.height
309
+ )
240
310
  const cls = pane === paneMap.terminal
241
311
  ? 'hide'
242
312
  : ''
@@ -328,12 +398,17 @@ export default class SessionWrapper extends Component {
328
398
  renderControl = () => {
329
399
  const { sftpPathFollowSsh } = this.state
330
400
  const { props } = this
331
- const { pane, enableSsh, type } = props.tab
332
- if (type === terminalRdpType || type === terminalVncType) {
401
+ const { tab } = props
402
+ const { pane, enableSsh, type } = tab
403
+ if (
404
+ type === terminalRdpType ||
405
+ type === terminalVncType ||
406
+ type === terminalWebType
407
+ ) {
333
408
  return null
334
409
  }
335
- const termType = props.tab?.type
336
- const isSsh = props.tab.authType
410
+ const termType = tab?.type
411
+ const isSsh = tab.authType
337
412
  const isLocal = !isSsh && (termType === connectionMap.local || !termType)
338
413
  const types = [
339
414
  paneMap.terminal,
@@ -410,14 +485,10 @@ export default class SessionWrapper extends Component {
410
485
  }
411
486
 
412
487
  render () {
413
- const {
414
- splitDirection
415
- } = this.state
416
488
  const { pane, id } = this.props.tab
417
489
  const cls = classnames(
418
490
  'term-sftp-box',
419
491
  pane,
420
- splitDirection,
421
492
  {
422
493
  'is-transporting': this.props.tab.isTransporting
423
494
  },
@@ -425,10 +496,17 @@ export default class SessionWrapper extends Component {
425
496
  'disable-ssh': this.props.tab.enableSsh === false
426
497
  }
427
498
  )
499
+ const divProps = {
500
+ className: cls,
501
+ id: `is-${id}`,
502
+ onDragEnter: this.onDragEnter,
503
+ onDragLeave: this.onDragLeave,
504
+ onDrop: this.onDrop,
505
+ onDragEnd: this.onDragEnd
506
+ }
428
507
  return (
429
508
  <div
430
- className={cls}
431
- id={`is-${id}`}
509
+ {...divProps}
432
510
  >
433
511
  {this.renderControl()}
434
512
  {this.renderTerminals()}
@@ -48,14 +48,18 @@
48
48
  &:hover
49
49
  color text-light
50
50
 
51
+ .sessions
52
+ position absolute
53
+ background main
51
54
  .session-wrap
52
55
  display none
53
- .session-current
56
+ position absolute
57
+ overflow hidden
58
+ z-index 3
59
+ padding-top 36px
60
+ .session-batch-active
54
61
  display block
55
- .no-sessions
56
- background main-dark
57
- text-align center
58
- padding 50px 0
62
+
59
63
  .web-session-wrap
60
64
  height 100vh
61
65
  background main