@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
@@ -1,416 +1,74 @@
1
1
  import { Component } from 'react'
2
2
  import Session from './session.jsx'
3
- import WebSession from '../web/web-session.jsx'
4
- import { findIndex, pick } from 'lodash-es'
3
+
4
+ import { pick } from 'lodash-es'
5
5
  import classNames from 'classnames'
6
- import generate from '../../common/id-with-stamp'
7
- import copy from 'json-deep-copy'
8
- import Tabs from '../tabs/index.jsx'
9
6
  import {
10
- tabActions,
11
- paneMap,
12
- statusMap,
13
- terminalWebType,
14
7
  termControlHeight
15
8
  } from '../../common/constants.js'
16
- import newTerm, { updateCount } from '../../common/new-terminal.js'
17
- import LogoElem from '../common/logo-elem.jsx'
18
- import { Button } from 'antd'
19
- import toSimpleObj from '../../common/to-simple-obj.js'
20
- import { shortcutExtend } from '../shortcuts/shortcut-handler.js'
21
- import deepEqual from 'fast-deep-equal'
22
-
23
- const e = window.translate
24
-
25
- class Sessions extends Component {
26
- constructor (props) {
27
- super(props)
28
- this.state = {
29
- tabs: copy(props.tabs || []),
30
- currentTabId: props.currentTabId
31
- }
32
- this.bindHandleKeyboardEvent = this.handleKeyboardEvent.bind(this)
33
- }
34
-
35
- componentDidMount () {
36
- this.watch()
37
- this.initShortcuts()
38
- }
39
-
40
- componentDidUpdate (prevProps) {
41
- if (
42
- this.props.tabs &&
43
- !deepEqual(prevProps.tabs, this.props.tabs)
44
- ) {
45
- this.updateTabs(this.props.tabs)
46
- }
47
- }
48
-
49
- componentWillUnmount () {
50
- window.removeEventListener('message', this.onEvent)
51
- window.removeEventListener('keydown', this.bindHandleKeyboardEvent)
52
- this.timer && clearTimeout(this.timer)
53
- this.timer = null
54
- }
55
-
56
- updateTabs = (propTabs) => {
57
- const update = {
58
- tabs: copy(propTabs)
59
- }
60
- const currentTab = propTabs.find(t => t.id === this.state.currentTabId)
61
- if (!currentTab) {
62
- update.currentTabId = propTabs[0]?.id
63
- }
64
- this.setState(update)
65
- }
66
-
67
- initShortcuts () {
68
- window.addEventListener('keydown', this.bindHandleKeyboardEvent)
69
- }
9
+ import pixed from '../layout/pixed'
70
10
 
71
- notCurrentTab = (tab) => {
72
- return this.state.currentTabId !== window.store.currentTabId
11
+ export default class Sessions extends Component {
12
+ // Function to reload a tab using store.reloadTab
13
+ reloadTab = (tab) => {
14
+ window.store.reloadTab(tab.id)
73
15
  }
74
16
 
75
- closeCurrentTabShortcut = (e) => {
76
- if (this.notCurrentTab()) {
77
- return
78
- }
79
- e.stopPropagation()
80
- this.delTab(
81
- this.state.currentTabId
82
- )
83
- }
84
-
85
- reloadCurrentTabShortcut = (e) => {
86
- if (this.notCurrentTab()) {
87
- return
88
- }
89
- e.stopPropagation()
90
- this.reloadTab(
91
- this.getCurrentTab()
92
- )
93
- }
94
-
95
- cloneToNextLayoutShortcut = (e) => {
96
- if (this.notCurrentTab()) {
97
- return
98
- }
99
- e.stopPropagation()
100
- window.store.cloneToNextLayout()
101
- }
102
-
103
- watch = () => {
104
- window.addEventListener('message', this.onEvent)
105
- }
106
-
107
- updateStoreTabs = (tabs) => {
108
- window.store.updateStoreTabs(tabs, this.props.batch)
109
- }
110
-
111
- updateStoreCurrentTabId = id => {
112
- if (id) {
113
- window.store.storeAssign({
114
- currentTabId: id,
115
- [id + this.props.batch]: id
116
- })
117
- this.setState({
118
- currentTabId: id
119
- })
120
- } else {
121
- document.querySelector('.tab.active')?.click()
122
- }
123
- }
124
-
125
- getCurrentTab = () => {
126
- const {
127
- currentTabId,
128
- tabs
129
- } = this.state
130
- return tabs.find(t => t.id === currentTabId)
131
- }
132
-
133
- editTab = (id, update) => {
134
- this.setState((oldState) => {
135
- const tabs = copy(oldState.tabs)
136
- const tab = tabs.find(t => t.id === id)
137
- if (tab) {
138
- Object.assign(tab, update)
139
- }
140
- return {
141
- tabs
142
- }
143
- }, () => {
144
- this.updateStoreTabs(this.state.tabs)
145
- })
146
- }
147
-
148
- addTab = (_tab, _index, callback) => {
149
- this.setState((oldState) => {
150
- const tabs = copy(oldState.tabs)
151
- const index = typeof _index === 'undefined'
152
- ? tabs.length
153
- : _index
154
- let tab = _tab
155
- if (!tab) {
156
- tab = newTerm()
157
- } else {
158
- updateCount(tab)
159
- }
160
- tab.batch = this.props.batch
161
- tabs.splice(index, 0, tab)
162
- return {
163
- currentTabId: tab.id,
164
- tabs
165
- }
166
- }, () => {
167
- this.updateStoreTabs(this.state.tabs)
168
- this.updateStoreCurrentTabId(this.state.currentTabId)
169
- if (callback) {
170
- callback()
171
- }
172
- })
173
- }
174
-
175
- // After
17
+ // Function to delete tab using store.delTab
176
18
  delTab = (id) => {
177
- this.setState((oldState) => {
178
- const tabs = copy(oldState.tabs)
179
- const { currentTabId } = oldState
180
- const up = {}
181
- if (currentTabId === id) {
182
- let i = findIndex(tabs, t => {
183
- return t.id === id
184
- })
185
- i = i ? i - 1 : i + 1
186
- const next = tabs[i] || {}
187
- up.currentTabId = next.id || ''
188
- }
189
- up.tabs = tabs.filter(t => {
190
- return t.id !== id
191
- })
192
- return up
193
- }, () => {
194
- this.updateStoreTabs(this.state.tabs)
195
- if (this.state.currentTabId !== id) {
196
- this.updateStoreCurrentTabId(this.state.currentTabId)
197
- }
198
- })
199
- }
200
-
201
- initFirstTab = () => {
202
- const tab = newTerm()
203
- const { batch } = this.props
204
- tab.batch = batch
205
- this.addTab(tab)
206
- }
207
-
208
- handleClick = () => {
209
- window.store.currentTabId = this.state.currentTabId
210
- }
211
-
212
- reloadTab = (tabToReload) => {
213
- const tab = copy(
214
- tabToReload
215
- )
216
- tab.pane = paneMap.terminal
217
- const { id } = tab
218
- const { tabs } = this.state
219
- tab.id = generate()
220
- tab.status = statusMap.processing
221
- const index = findIndex(tabs, t => t.id === id)
222
- this.addTab(tab, index, () => {
223
- this.delTab(id)
224
- this.onChangeTabId(tab.id)
225
- })
226
- }
227
-
228
- onDuplicateTab = (tabToDup) => {
229
- const defaultStatus = statusMap.processing
230
- let tab = copy(tabToDup)
231
- updateCount(tab)
232
- const tabs = copy(this.state.tabs)
233
- const index = findIndex(
234
- tabs,
235
- d => d.id === tab.id
236
- )
237
- tab = {
238
- ...tab,
239
- status: defaultStatus,
240
- id: generate(),
241
- isTransporting: undefined
242
- }
243
- tab.pane = paneMap.terminal
244
- this.addTab(tab, index + 1)
245
- }
246
-
247
- onChangeTabId = id => {
248
- const matchedTab = this.state.tabs.find(t => t.id === id)
249
- if (!matchedTab) {
250
- return
251
- }
252
-
253
- // Batch the updates
254
- this.setState({
255
- currentTabId: id
256
- }, () => {
257
- this.updateStoreCurrentTabId(id)
258
- this.timer = setTimeout(window.store.triggerResize, 500)
259
- this.postChange()
260
- })
261
- }
262
-
263
- setTabs = tabs => {
264
- this.setState({
265
- tabs
266
- })
267
- this.updateStoreTabs(tabs)
268
- }
269
-
270
- setOffline = () => {
271
- this.setState(oldState => {
272
- const tabs = copy(oldState.tabs)
273
- .map(t => {
274
- return {
275
- ...t,
276
- status: t.host ? statusMap.error : t.status
277
- }
278
- })
279
- this.updateStoreTabs(tabs)
280
- return {
281
- tabs
282
- }
283
- })
19
+ window.store.delTab(id)
284
20
  }
285
21
 
286
- updateTabsStatus = tabIds => {
287
- this.setState(oldState => {
288
- const tabs = copy(oldState.tabs).map(d => {
289
- return {
290
- ...d,
291
- isTransporting: tabIds.includes(d.id)
292
- }
293
- })
294
- this.updateStoreTabs(tabs)
295
- return {
296
- tabs
297
- }
298
- })
299
- }
300
-
301
- onEvent = e => {
302
- const {
303
- currentTabId,
304
- action,
305
- id,
306
- update,
307
- tab,
308
- index,
309
- batch,
310
- tabIds
311
- } = e.data || {}
312
- if (
313
- action === tabActions.changeCurrentTabId &&
314
- currentTabId &&
315
- currentTabId !== this.state.currentTabId
316
- ) {
317
- this.onChangeTabId(currentTabId)
318
- } else if (action === tabActions.updateTabs) {
319
- this.editTab(id, update)
320
- } else if (action === tabActions.addTab && (batch ?? tab.batch) === this.props.batch) {
321
- this.addTab(tab, index)
322
- } else if (action === tabActions.initFirstTab) {
323
- this.initFirstTab()
324
- } else if (action === tabActions.delTab) {
325
- this.delTab(id)
326
- } else if (action === tabActions.setAllTabOffline) {
327
- this.setOffline()
328
- } else if (action === tabActions.updateTabsStatus) {
329
- this.updateTabsStatus(tabIds)
330
- }
331
- }
332
-
333
- postChange = () => {
334
- window.store.currentLayoutBatch = this.props.batch
335
- window.store.triggerResize()
336
- }
337
-
338
- handleNewTab = () => {
339
- this.initFirstTab()
340
- }
341
-
342
- handleNewSsh = () => {
343
- window.store.onNewSsh()
344
- }
345
-
346
- renderNoSession = () => {
347
- const props = {
348
- style: {
349
- height: this.props.height + 'px'
350
- }
351
- }
352
- return (
353
- <div className='no-sessions electerm-logo-bg' {...props}>
354
- <Button
355
- onClick={this.handleNewTab}
356
- size='large'
357
- className='mg1r mg1b add-new-tab-btn'
358
- >
359
- {e('newTab')}
360
- </Button>
361
- <Button
362
- onClick={this.handleNewSsh}
363
- size='large'
364
- className='mg1r mg1b'
365
- >
366
- {e('newBookmark')}
367
- </Button>
368
- <div className='pd3'>
369
- <LogoElem />
370
- </div>
371
- </div>
372
- )
22
+ // Function to edit tab properties using store.editItem
23
+ editTab = (id, update) => {
24
+ window.store.updateTab(id, update)
373
25
  }
374
26
 
375
- computeHeight = () => {
27
+ computeHeight = (height) => {
376
28
  const {
377
29
  tabsHeight
378
30
  } = this.props
379
- return this.props.height -
31
+ return height -
380
32
  tabsHeight -
381
33
  termControlHeight
382
34
  }
383
35
 
36
+ computeSessionStyle = (batch) => {
37
+ const style = this.props.styles[batch]
38
+ return pixed(style)
39
+ }
40
+
384
41
  renderSessions () {
385
42
  const {
386
- config, width, height
387
- } = this.props
388
- const {
43
+ config,
44
+ tabs,
389
45
  currentTabId,
390
- tabs
391
- } = this.state
392
- if (!tabs || !tabs.length) {
393
- return this.renderNoSession()
394
- }
46
+ sizes
47
+ } = this.props
395
48
  return tabs.map((tab) => {
396
- const { id, type } = tab
49
+ const { id, batch } = tab
50
+ const { height, width } = sizes[batch]
51
+ const currentBatchTabId = this.props['currentTabId' + batch]
397
52
  const cls = classNames(
398
53
  `session-wrap session-${id}`,
399
54
  {
400
- 'session-current': id === currentTabId
55
+ 'session-current': id === currentTabId,
56
+ 'session-batch-active': id === currentBatchTabId
401
57
  }
402
58
  )
59
+ const sessionWrapProps = {
60
+ style: this.computeSessionStyle(batch),
61
+ className: cls
62
+ }
403
63
  const sessProps = {
404
64
  currentTabId,
405
- tab: toSimpleObj(tab),
65
+ tab,
406
66
  width,
407
67
  height,
408
68
  ...pick(this.props, [
409
- 'batch',
410
69
  'resolutions',
411
70
  'hideDelKeyTip',
412
71
  'fileOperation',
413
- 'file',
414
72
  'pinnedQuickCommandBar',
415
73
  'tabsHeight',
416
74
  'appPath',
@@ -420,34 +78,15 @@ class Sessions extends Component {
420
78
  ]),
421
79
  config,
422
80
  ...pick(this, [
423
- 'onChangeTabId',
424
- 'onDuplicateTab',
425
81
  'reloadTab',
426
82
  'computeHeight',
427
83
  'delTab',
428
- 'addTab',
429
84
  'editTab'
430
- ])
431
- }
432
- if (type === terminalWebType) {
433
- const webProps = {
434
- tab,
435
- width,
436
- height: this.computeHeight(),
437
- ...pick(this, [
438
- 'reloadTab'
439
- ])
440
- }
441
- return (
442
- <div className={cls} key={id}>
443
- <WebSession
444
- {...webProps}
445
- />
446
- </div>
447
- )
85
+ ]),
86
+ currentBatchTabId
448
87
  }
449
88
  return (
450
- <div className={cls} key={id}>
89
+ <div {...sessionWrapProps} key={id}>
451
90
  <Session
452
91
  {...sessProps}
453
92
  />
@@ -456,66 +95,21 @@ class Sessions extends Component {
456
95
  })
457
96
  }
458
97
 
459
- renderTabs = () => {
460
- const {
461
- config,
462
- width,
463
- height,
464
- batch,
465
- layout,
466
- isMaximized
467
- } = this.props
468
- const {
469
- tabs,
470
- currentTabId
471
- } = this.state
472
- const tabsProps = {
473
- batch,
474
- currentTabId,
475
- config,
476
- width,
477
- height,
478
- layout,
479
- isMaximized,
480
- tabs,
481
- ...pick(this, [
482
- 'setTabs',
483
- 'onChangeTabId',
484
- 'onDuplicateTab',
485
- 'reloadTab',
486
- 'delTab',
487
- 'addTab',
488
- 'editTab'
489
- ])
98
+ render () {
99
+ const { layoutStyle, tabs } = this.props
100
+ if (!tabs || !tabs.length) {
101
+ return null
102
+ }
103
+ const sessProps = {
104
+ style: layoutStyle,
105
+ className: 'sessions'
490
106
  }
491
- return (
492
- <Tabs
493
- key={'main-tabs' + batch}
494
- {...tabsProps}
495
- />
496
- )
497
- }
498
-
499
- renderSessionsWrap = () => {
500
107
  return (
501
108
  <div
502
- className='sessions'
503
- key='main-sess'
504
- onClick={this.handleClick}
109
+ {...sessProps}
505
110
  >
506
111
  {this.renderSessions()}
507
112
  </div>
508
113
  )
509
114
  }
510
-
511
- render () {
512
- return (
513
- <div>
514
- {this.renderTabs()}
515
- {this.renderSessionsWrap()}
516
- </div>
517
- )
518
- }
519
115
  }
520
-
521
- export default shortcutExtend(Sessions)
@@ -105,7 +105,8 @@ export default auto(function SettingModalWrap (props) {
105
105
  items,
106
106
  onChange: store.handleChangeSettingTab,
107
107
  destroyInactiveTabPane: true,
108
- className: 'setting-tabs'
108
+ className: 'setting-tabs',
109
+ type: 'card'
109
110
  }
110
111
  return (
111
112
  <div>
@@ -212,7 +212,7 @@ export default class Sftp extends Component {
212
212
  }
213
213
 
214
214
  isActive () {
215
- return this.props.enableSftp && this.props.currentTabId === this.props.tab.id &&
215
+ return this.props.enableSftp && this.props.currentBatchTabId === this.props.tab.id &&
216
216
  this.props.pane === paneMap.fileManager
217
217
  }
218
218
 
@@ -13,6 +13,24 @@ class ShortcutControl extends React.PureComponent {
13
13
  window.addEventListener('mousewheel', this.handleKeyboardEvent.bind(this))
14
14
  }
15
15
 
16
+ closeCurrentTabShortcut = throttle((e) => {
17
+ e.stopPropagation()
18
+ const { currentTabId } = window.store
19
+ if (currentTabId) {
20
+ window.store.delTab(currentTabId)
21
+ }
22
+ }, 500)
23
+
24
+ reloadCurrentTabShortcut = throttle((e) => {
25
+ e.stopPropagation()
26
+ window.store.reloadTab()
27
+ }, 500)
28
+
29
+ cloneToNextLayoutShortcut = throttle((e) => {
30
+ e.stopPropagation()
31
+ window.store.cloneToNextLayout()
32
+ }, 500)
33
+
16
34
  prevTabShortcut = throttle((e) => {
17
35
  e.stopPropagation()
18
36
  window.store.clickPrevTab()
@@ -35,12 +53,6 @@ class ShortcutControl extends React.PureComponent {
35
53
  x && x.click()
36
54
  }, 500)
37
55
 
38
- splitShortcut = throttle((e) => {
39
- e.stopPropagation()
40
- const x = document.querySelector('.session-current .icon-split')
41
- x && x.click()
42
- }, 1000)
43
-
44
56
  zoominShortcut = throttle((e) => {
45
57
  e.stopPropagation()
46
58
  window.store.zoom(0.25, true)
@@ -9,7 +9,8 @@ import {
9
9
  InfoCircleOutlined,
10
10
  AlignLeftOutlined,
11
11
  BugOutlined,
12
- HeartOutlined
12
+ HeartOutlined,
13
+ JavaScriptOutlined
13
14
  } from '@ant-design/icons'
14
15
  import { Modal, Tabs, Button } from 'antd'
15
16
  import Link from '../common/external-link'
@@ -174,6 +175,12 @@ export default memo(function InfoModal (props) {
174
175
  {sponsorLink}
175
176
  </Link>
176
177
  </p>
178
+ <p className='mg1b'>
179
+ <JavaScriptOutlined /> <b className='mg1r'>Powered by</b>
180
+ <Link to='https://github.com/tylerlong/manate'>
181
+ manate
182
+ </Link>
183
+ </p>
177
184
  {renderCheckUpdate()}
178
185
  </div>
179
186
  )
@@ -39,7 +39,7 @@ export default class SidePanel extends PureComponent {
39
39
  el.style.width = nw + 'px'
40
40
  const el1 = document.querySelector('.sessions')
41
41
  if (el1) {
42
- el1.style.marginLeft = (nw + 43) + 'px'
42
+ el1.style.left = (nw + 43) + 'px'
43
43
  }
44
44
  }
45
45