@electerm/electerm-react 1.50.66 → 1.51.1

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 +99 -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 +10 -5
  23. package/client/components/terminal/terminal-interactive.jsx +1 -7
  24. package/client/components/terminal/terminal.styl +7 -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::before {
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::before {
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,25 @@ 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
+ }
205
+ return (
206
+ <WebSession
207
+ {...webProps}
208
+ />
209
+ )
210
+ }
150
211
  if (type === terminalRdpType || type === terminalVncType) {
151
212
  const rdpProps = {
152
213
  tab: this.props.tab,
@@ -186,9 +247,10 @@ export default class SessionWrapper extends Component {
186
247
  const cls = pane === paneMap.terminal
187
248
  ? 'terms-box'
188
249
  : 'terms-box hide'
189
- const height = this.props.computeHeight()
190
- const { tab } = this.props
191
250
  const width = this.getWidth()
251
+ const height = this.props.computeHeight(
252
+ this.props.height
253
+ )
192
254
  const themeConfig = copy(window.store.getThemeConfig())
193
255
  const logName = safeName(`${tab.title ? tab.title + '_' : ''}${tab.host ? tab.host + '_' : ''}${tab.id}`)
194
256
  const pops = {
@@ -203,7 +265,9 @@ export default class SessionWrapper extends Component {
203
265
  'setCwd',
204
266
  'onDelKeyPressed'
205
267
  ]),
206
- ...this.computePosition()
268
+ ...this.computePosition(),
269
+ width,
270
+ height
207
271
  }
208
272
  return (
209
273
  <div
@@ -214,7 +278,6 @@ export default class SessionWrapper extends Component {
214
278
  }}
215
279
  >
216
280
  <Term
217
- key={tab.id}
218
281
  logName={logName}
219
282
  sessionId={sessionId}
220
283
  sessionOptions={sessionOptions}
@@ -233,10 +296,16 @@ export default class SessionWrapper extends Component {
233
296
  cwd
234
297
  } = this.state
235
298
  const { pane, type, id } = this.props.tab
236
- if (type === terminalRdpType) {
299
+ if (
300
+ type === terminalRdpType ||
301
+ type === terminalVncType ||
302
+ type === terminalWebType
303
+ ) {
237
304
  return null
238
305
  }
239
- const height = this.props.computeHeight(pane)
306
+ const height = this.props.computeHeight(
307
+ this.props.height
308
+ )
240
309
  const cls = pane === paneMap.terminal
241
310
  ? 'hide'
242
311
  : ''
@@ -328,12 +397,17 @@ export default class SessionWrapper extends Component {
328
397
  renderControl = () => {
329
398
  const { sftpPathFollowSsh } = this.state
330
399
  const { props } = this
331
- const { pane, enableSsh, type } = props.tab
332
- if (type === terminalRdpType || type === terminalVncType) {
400
+ const { tab } = props
401
+ const { pane, enableSsh, type } = tab
402
+ if (
403
+ type === terminalRdpType ||
404
+ type === terminalVncType ||
405
+ type === terminalWebType
406
+ ) {
333
407
  return null
334
408
  }
335
- const termType = props.tab?.type
336
- const isSsh = props.tab.authType
409
+ const termType = tab?.type
410
+ const isSsh = tab.authType
337
411
  const isLocal = !isSsh && (termType === connectionMap.local || !termType)
338
412
  const types = [
339
413
  paneMap.terminal,
@@ -410,14 +484,10 @@ export default class SessionWrapper extends Component {
410
484
  }
411
485
 
412
486
  render () {
413
- const {
414
- splitDirection
415
- } = this.state
416
487
  const { pane, id } = this.props.tab
417
488
  const cls = classnames(
418
489
  'term-sftp-box',
419
490
  pane,
420
- splitDirection,
421
491
  {
422
492
  'is-transporting': this.props.tab.isTransporting
423
493
  },
@@ -425,10 +495,17 @@ export default class SessionWrapper extends Component {
425
495
  'disable-ssh': this.props.tab.enableSsh === false
426
496
  }
427
497
  )
498
+ const divProps = {
499
+ className: cls,
500
+ id: `is-${id}`,
501
+ onDragEnter: this.onDragEnter,
502
+ onDragLeave: this.onDragLeave,
503
+ onDrop: this.onDrop,
504
+ onDragEnd: this.onDragEnd
505
+ }
428
506
  return (
429
507
  <div
430
- className={cls}
431
- id={`is-${id}`}
508
+ {...divProps}
432
509
  >
433
510
  {this.renderControl()}
434
511
  {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