@electerm/electerm-react 1.50.21 → 1.50.31

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,7 +1,13 @@
1
+ import {
2
+ tabActions
3
+ } from '../../common/constants'
4
+ import postMsg from '../../common/post-msg'
5
+
1
6
  export default function LayoutItem (props) {
2
7
  const {
3
8
  children,
4
9
  i,
10
+ batch,
5
11
  ...itemProps
6
12
  } = props
7
13
  function handleClick (e) {
@@ -12,13 +18,75 @@ export default function LayoutItem (props) {
12
18
  }
13
19
  currentElement = currentElement.parentElement
14
20
  }
21
+ window.store.currentLayoutBatch = i
22
+ }
23
+
24
+ function getDom () {
25
+ return document.querySelector(`.layout-item.v${batch + 1}`)
26
+ }
27
+
28
+ function onDrop (e) {
29
+ e.preventDefault()
30
+ const { target } = e
31
+ if (!target) {
32
+ return
33
+ }
34
+ let currentElement = target
35
+ while (currentElement) {
36
+ if (currentElement.classList && currentElement.classList.contains('tab')) {
37
+ return
38
+ }
39
+ currentElement = currentElement.parentElement
40
+ }
41
+ // debug('target drop', target)
42
+ const fromTab = JSON.parse(e.dataTransfer.getData('fromFile'))
43
+ const onDropElem = getDom
44
+ if (!onDropElem || !fromTab || fromTab.batch === batch) {
45
+ return
46
+ }
47
+ const { store } = window
48
+ const { tabs } = store
49
+ const t = tabs.find(t => t.id === fromTab.id)
50
+ if (!t) {
51
+ return
52
+ }
53
+ t.batch = batch
54
+ postMsg({
55
+ action: tabActions.changeCurrentTabId,
56
+ currentTabId: store.currentTabId
57
+ })
58
+ store.setTabs(tabs)
59
+ clearCls()
60
+ }
61
+
62
+ function clearCls () {
63
+ getDom()?.classList.remove('drag-over')
64
+ }
65
+
66
+ function addCls () {
67
+ getDom()?.classList.add('drag-over')
68
+ }
69
+
70
+ function onDragEnter () {
71
+ addCls()
72
+ }
73
+
74
+ function onDragLeave (e) {
75
+ clearCls()
76
+ }
15
77
 
16
- window.StorageEvent.currentLayoutBatch = i
78
+ function onDragEnd (e) {
79
+ clearCls()
80
+ e && e.dataTransfer && e.dataTransfer.clearData()
17
81
  }
18
82
  return (
19
83
  <div
20
84
  {...itemProps}
21
85
  onClick={handleClick}
86
+ onDragEnter={onDragEnter}
87
+ onDragLeave={onDragLeave}
88
+ onDragEnd={onDragEnd}
89
+ onDrop={onDrop}
22
90
  >
23
91
  {children}
24
92
  </div>
@@ -84,7 +84,7 @@ export default auto(function Layout (props) {
84
84
  batch: i,
85
85
  layout,
86
86
  ...v,
87
- tabs: tabsBatch[i],
87
+ tabs: tabsBatch[i] || [],
88
88
  ...pick(store, [
89
89
  'isMaximized',
90
90
  'config',
@@ -2,4 +2,7 @@
2
2
  .layout-wrap
3
3
  .layout-item
4
4
  position absolute
5
- background main
5
+ background main
6
+ overflow hidden
7
+ &.drag-over
8
+ border 2px solid #08c
@@ -37,7 +37,8 @@ export default memo(function LayoutWrap (props) {
37
37
  const itemProps = {
38
38
  i,
39
39
  style: pixed(wrapStyles[i]),
40
- className: 'layout-item v' + (i + 1)
40
+ className: 'layout-item v' + (i + 1),
41
+ batch: i
41
42
  }
42
43
  return (
43
44
  <LayoutItem
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import { useEffect } from 'react'
6
- import { useDelta, useConditionalEffect } from 'react-delta'
6
+ import { useDelta, useConditionalEffect } from 'react-delta-hooks'
7
7
  import eq from 'fast-deep-equal'
8
8
 
9
9
  const themeDomId = 'custom-css'
@@ -64,7 +64,7 @@ export default auto(function Index (props) {
64
64
  if (window.et.isWebApp) {
65
65
  window.onbeforeunload = store.beforeExit
66
66
  }
67
- store.isSencondInstance = window.pre.runSync('isSencondInstance')
67
+ store.isSecondInstance = window.pre.runSync('isSecondInstance')
68
68
  store.initData()
69
69
  store.checkForDbUpgrade()
70
70
  window.pre.runGlobalAsync('registerDeepLink')
@@ -76,7 +76,7 @@ export default auto(function Index (props) {
76
76
  config,
77
77
  terminalFullScreen,
78
78
  pinned,
79
- isSencondInstance,
79
+ isSecondInstance,
80
80
  pinnedQuickCommandBar,
81
81
  wsInited,
82
82
  upgradeInfo,
@@ -98,7 +98,7 @@ export default auto(function Index (props) {
98
98
  pinned,
99
99
  'qm-pinned': pinnedQuickCommandBar,
100
100
  'term-fullscreen': terminalFullScreen,
101
- 'is-main': !isSencondInstance
101
+ 'is-main': !isSecondInstance
102
102
  })
103
103
  const ext1 = {
104
104
  className: cls
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import { useEffect } from 'react'
6
- import { useDelta, useConditionalEffect } from 'react-delta'
6
+ import { useDelta, useConditionalEffect } from 'react-delta-hooks'
7
7
  import eq from 'fast-deep-equal'
8
8
 
9
9
  const themeDomId = 'theme-css'
@@ -20,7 +20,6 @@ import generate from '../../common/uid'
20
20
  import copy from 'json-deep-copy'
21
21
  import classnames from 'classnames'
22
22
  import {
23
- termControlHeight,
24
23
  paneMap,
25
24
  terminalActions,
26
25
  connectionMap,
@@ -89,15 +88,6 @@ export default class SessionWrapper extends Component {
89
88
  })
90
89
  }
91
90
 
92
- computeHeight = () => {
93
- const {
94
- tabsHeight
95
- } = this.props
96
- return this.props.height -
97
- tabsHeight -
98
- termControlHeight
99
- }
100
-
101
91
  editTab = (up) => {
102
92
  const {
103
93
  tab,
@@ -131,7 +121,7 @@ export default class SessionWrapper extends Component {
131
121
 
132
122
  computePosition = (index) => {
133
123
  const windowWidth = this.getWidth()
134
- const heightAll = this.computeHeight()
124
+ const heightAll = this.props.computeHeight()
135
125
  return {
136
126
  height: heightAll,
137
127
  width: windowWidth,
@@ -196,7 +186,7 @@ export default class SessionWrapper extends Component {
196
186
  const cls = pane === paneMap.terminal
197
187
  ? 'terms-box'
198
188
  : 'terms-box hide'
199
- const height = this.computeHeight()
189
+ const height = this.props.computeHeight()
200
190
  const { tab } = this.props
201
191
  const width = this.getWidth()
202
192
  const themeConfig = copy(window.store.getThemeConfig())
@@ -246,7 +236,7 @@ export default class SessionWrapper extends Component {
246
236
  if (type === terminalRdpType) {
247
237
  return null
248
238
  }
249
- const height = this.computeHeight(pane)
239
+ const height = this.props.computeHeight(pane)
250
240
  const cls = pane === paneMap.terminal
251
241
  ? 'hide'
252
242
  : ''
@@ -8,15 +8,13 @@ import copy from 'json-deep-copy'
8
8
  import wait from '../../common/wait.js'
9
9
  import Tabs from '../tabs/index.jsx'
10
10
  import {
11
- commonActions,
12
11
  tabActions,
13
12
  paneMap,
14
13
  statusMap,
15
- terminalWebType
14
+ terminalWebType,
15
+ termControlHeight
16
16
  } from '../../common/constants.js'
17
17
  import newTerm, { updateCount } from '../../common/new-terminal.js'
18
- import postMsg from '../../common/post-msg.js'
19
-
20
18
  import LogoElem from '../common/logo-elem.jsx'
21
19
  import { Button } from 'antd'
22
20
  import toSimpleObj from '../../common/to-simple-obj.js'
@@ -45,9 +43,7 @@ class Sessions extends Component {
45
43
  this.props.tabs &&
46
44
  !deepEqual(prevProps.tabs, this.props.tabs)
47
45
  ) {
48
- this.setState({
49
- tabs: copy(this.props.tabs)
50
- })
46
+ this.updateTabs(this.props.tabs)
51
47
  }
52
48
  }
53
49
 
@@ -58,11 +54,29 @@ class Sessions extends Component {
58
54
  this.timer = null
59
55
  }
60
56
 
57
+ updateTabs = (propTabs) => {
58
+ const update = {
59
+ tabs: copy(propTabs)
60
+ }
61
+ const currentTab = propTabs.find(t => t.id === this.state.currentTabId)
62
+ if (!currentTab) {
63
+ update.currentTabId = propTabs[0]?.id
64
+ }
65
+ this.setState(update)
66
+ }
67
+
61
68
  initShortcuts () {
62
69
  window.addEventListener('keydown', this.bindHandleKeyboardEvent)
63
70
  }
64
71
 
72
+ notCurrentTab = (tab) => {
73
+ return this.state.currentTabId !== window.store.currentTabId
74
+ }
75
+
65
76
  closeCurrentTabShortcut = (e) => {
77
+ if (this.notCurrentTab()) {
78
+ return
79
+ }
66
80
  e.stopPropagation()
67
81
  this.delTab(
68
82
  this.state.currentTabId
@@ -70,12 +84,23 @@ class Sessions extends Component {
70
84
  }
71
85
 
72
86
  reloadCurrentTabShortcut = (e) => {
87
+ if (this.notCurrentTab()) {
88
+ return
89
+ }
73
90
  e.stopPropagation()
74
91
  this.reloadTab(
75
92
  this.getCurrentTab()
76
93
  )
77
94
  }
78
95
 
96
+ cloneToNextLayoutShortcut = (e) => {
97
+ if (this.notCurrentTab()) {
98
+ return
99
+ }
100
+ e.stopPropagation()
101
+ window.store.cloneToNextLayout()
102
+ }
103
+
79
104
  watch = () => {
80
105
  window.addEventListener('message', this.onEvent)
81
106
  }
@@ -94,18 +119,8 @@ class Sessions extends Component {
94
119
  currentTabId: id
95
120
  })
96
121
  } else {
97
- window.store.focus()
122
+ document.querySelector('.tab.active').click()
98
123
  }
99
- postMsg({
100
- action: commonActions.updateStore,
101
- value: id,
102
- prop: 'currentTabId'
103
- })
104
- postMsg({
105
- action: commonActions.updateStore,
106
- value: id,
107
- prop: 'currentTabId' + this.props.batch
108
- })
109
124
  }
110
125
 
111
126
  getCurrentTab = () => {
@@ -185,6 +200,10 @@ class Sessions extends Component {
185
200
  this.addTab(tab)
186
201
  }
187
202
 
203
+ handleClick = () => {
204
+ window.store.currentTabId = this.state.currentTabId
205
+ }
206
+
188
207
  reloadTab = async (tabToReload) => {
189
208
  this.setState(async oldState => {
190
209
  const tab = copy(
@@ -347,6 +366,15 @@ class Sessions extends Component {
347
366
  )
348
367
  }
349
368
 
369
+ computeHeight = () => {
370
+ const {
371
+ tabsHeight
372
+ } = this.props
373
+ return this.props.height -
374
+ tabsHeight -
375
+ termControlHeight
376
+ }
377
+
350
378
  renderSessions () {
351
379
  const {
352
380
  config, width, height
@@ -389,6 +417,7 @@ class Sessions extends Component {
389
417
  'onChangeTabId',
390
418
  'onDuplicateTab',
391
419
  'reloadTab',
420
+ 'computeHeight',
392
421
  'delTab',
393
422
  'addTab',
394
423
  'editTab'
@@ -396,7 +425,12 @@ class Sessions extends Component {
396
425
  }
397
426
  if (type === terminalWebType) {
398
427
  const webProps = {
399
- tab
428
+ tab,
429
+ width,
430
+ height: this.computeHeight(),
431
+ ...pick(this, [
432
+ 'reloadTab'
433
+ ])
400
434
  }
401
435
  return (
402
436
  <div className={cls} key={id}>
@@ -461,6 +495,7 @@ class Sessions extends Component {
461
495
  <div
462
496
  className='sessions'
463
497
  key='main-sess'
498
+ onClick={this.handleClick}
464
499
  >
465
500
  {this.renderSessions()}
466
501
  </div>
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * bookmark form
7
7
  */
8
- import { useDelta, useConditionalEffect } from 'react-delta'
8
+ import { useDelta, useConditionalEffect } from 'react-delta-hooks'
9
9
  import { ArrowDownOutlined, ArrowUpOutlined, SaveOutlined, ClearOutlined } from '@ant-design/icons'
10
10
  import { Button, Input, notification, Form } from 'antd'
11
11
  import Link from '../common/external-link'
@@ -10,6 +10,11 @@ export default () => {
10
10
  shortcut: 'alt+r',
11
11
  shortcutMac: 'alt+r'
12
12
  },
13
+ {
14
+ name: 'app_cloneToNextLayout',
15
+ shortcut: 'ctrl+/',
16
+ shortcutMac: 'meta+/'
17
+ },
13
18
  {
14
19
  name: 'app_newBookmark',
15
20
  shortcut: 'ctrl+n',
@@ -40,11 +45,6 @@ export default () => {
40
45
  shortcut: 'ctrl+tab',
41
46
  shortcutMac: 'ctrl+tab'
42
47
  },
43
- {
44
- name: 'terminal_split',
45
- shortcut: 'ctrl+/',
46
- shortcutMac: 'meta+/'
47
- },
48
48
  {
49
49
  name: 'terminal_clear',
50
50
  shortcut: 'ctrl+l,ctrl+shift+l',
@@ -9,7 +9,6 @@ import {
9
9
  Loading3QuartersOutlined,
10
10
  BorderlessTableOutlined
11
11
  } from '@ant-design/icons'
12
- import generate from '../../common/id-with-stamp'
13
12
  import { Tooltip, message } from 'antd'
14
13
  import classnames from 'classnames'
15
14
  import copy from 'json-deep-copy'
@@ -18,10 +17,7 @@ import Input from '../common/input-auto-focus'
18
17
  import createName from '../../common/create-title'
19
18
  import { addClass, removeClass } from '../../common/class'
20
19
  import {
21
- terminalSshConfigType,
22
- splitConfig,
23
- paneMap,
24
- statusMap
20
+ terminalSshConfigType
25
21
  } from '../../common/constants'
26
22
  import { shortcutDescExtend } from '../shortcuts/shortcut-handler.js'
27
23
 
@@ -203,18 +199,7 @@ class Tab extends Component {
203
199
  }
204
200
 
205
201
  cloneToNextLayout = () => {
206
- const defaultStatus = statusMap.processing
207
- const { batch, layout } = this.props
208
- const ntb = copy(this.state.tab)
209
- Object.assign(ntb, {
210
- id: generate(),
211
- status: defaultStatus,
212
- isTransporting: undefined,
213
- pane: paneMap.terminal
214
- })
215
- const maxBatch = splitConfig[layout].children
216
- ntb.batch = (batch + 1) % maxBatch
217
- window.store.addTab(ntb)
202
+ window.store.cloneToNextLayout()
218
203
  }
219
204
 
220
205
  newTab = () => {
@@ -280,17 +265,20 @@ class Tab extends Component {
280
265
  }
281
266
 
282
267
  renderContext = () => {
283
- const { tabs, tab, layout } = this.props
268
+ const { tabs, tab } = this.props
284
269
  const len = tabs.length
285
270
  const index = findIndex(tabs, t => t.id === tab.id)
286
271
  const noRight = index >= len - 1
287
272
  const isSshConfig = tab.type === terminalSshConfigType
288
273
  const res = []
289
274
  const reloadShortcut = this.getShortcut('app_reloadCurrentTab')
275
+ const closeShortcut = this.getShortcut('app_closeCurrentTab')
276
+ const cloneToNextShortcut = this.getShortcut('app_cloneToNextLayout')
290
277
  res.push({
291
278
  func: 'handleClose',
292
279
  icon: 'CloseOutlined',
293
- text: e('close')
280
+ text: e('close'),
281
+ subText: closeShortcut
294
282
  })
295
283
  res.push({
296
284
  func: 'closeOther',
@@ -314,13 +302,12 @@ class Tab extends Component {
314
302
  icon: 'CopyOutlined',
315
303
  text: e('duplicate')
316
304
  })
317
- if (layout !== 'c1') {
318
- res.push({
319
- func: 'cloneToNextLayout',
320
- icon: 'CopyOutlined',
321
- text: e('cloneToNextLayout')
322
- })
323
- }
305
+ res.push({
306
+ func: 'cloneToNextLayout',
307
+ icon: 'CopyOutlined',
308
+ text: e('cloneToNextLayout'),
309
+ subText: cloneToNextShortcut
310
+ })
324
311
  res.push({
325
312
  disabled: isSshConfig,
326
313
  func: 'doRename',
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * customize AttachAddon
3
3
  */
4
- import { AttachAddon } from 'xterm-addon-attach'
4
+ import { AttachAddon } from '@xterm/addon-attach'
5
5
  import regEscape from 'escape-string-regexp'
6
6
 
7
7
  export default class AttachAddonCustom extends AttachAddon {
@@ -31,24 +31,24 @@ import {
31
31
  } from '../../common/constants'
32
32
  import deepCopy from 'json-deep-copy'
33
33
  import { readClipboardAsync, copy } from '../../common/clipboard'
34
- import { FitAddon } from 'xterm-addon-fit'
34
+ import { FitAddon } from '@xterm/addon-fit'
35
35
  import AttachAddon from './attach-addon-custom'
36
- import { SearchAddon } from 'xterm-addon-search'
37
- import { WebLinksAddon } from 'xterm-addon-web-links'
38
- import { CanvasAddon } from 'xterm-addon-canvas'
39
- import { WebglAddon } from 'xterm-addon-webgl'
40
- import { LigaturesAddon } from 'xterm-addon-ligatures'
36
+ import { SearchAddon } from '@xterm/addon-search'
37
+ import { WebLinksAddon } from '@xterm/addon-web-links'
38
+ import { CanvasAddon } from '@xterm/addon-canvas'
39
+ import { WebglAddon } from '@xterm/addon-webgl'
40
+ import { LigaturesAddon } from '@xterm/addon-ligatures'
41
41
  import getProxy from '../../common/get-proxy'
42
42
  import { AddonZmodem } from './xterm-zmodem'
43
- import { Unicode11Addon } from 'xterm-addon-unicode11'
43
+ import { Unicode11Addon } from '@xterm/addon-unicode11'
44
44
  import keyControlPressed from '../../common/key-control-pressed'
45
- import { Terminal } from 'xterm'
45
+ import { Terminal } from '@xterm/xterm'
46
46
  import NormalBuffer from './normal-buffer'
47
47
  import { createTerm, resizeTerm } from './terminal-apis'
48
48
  import { shortcutExtend, shortcutDescExtend } from '../shortcuts/shortcut-handler.js'
49
49
  import { KeywordHighlighterAddon } from './highlight-addon.js'
50
50
  import { getLocalFileInfo } from '../sftp/file-read.js'
51
- import { SerializeAddon } from 'xterm-addon-serialize'
51
+ import { SerializeAddon } from '@xterm/addon-serialize'
52
52
  import strip from '@electerm/strip-ansi'
53
53
  import { formatBytes } from '../../common/byte-format.js'
54
54
  import * as fs from './fs.js'
@@ -1237,6 +1237,7 @@ clear\r`
1237
1237
  new KeywordHighlighterAddon(keywords)
1238
1238
  )
1239
1239
  window.store.triggerResize()
1240
+ window.store.focus()
1240
1241
  }
1241
1242
 
1242
1243
  onResize = throttle(() => {
@@ -0,0 +1,50 @@
1
+ import {
2
+ Input,
3
+ Tooltip
4
+ } from 'antd'
5
+ import { copy } from '../../common/clipboard'
6
+ import {
7
+ ReloadOutlined,
8
+ GlobalOutlined
9
+ } from '@ant-design/icons'
10
+
11
+ export default function AddressBar (props) {
12
+ const {
13
+ url,
14
+ onReload,
15
+ onOpen,
16
+ title,
17
+ description
18
+ } = props
19
+ const content = (
20
+ <div>
21
+ <h1>{title}</h1>
22
+ <p>{description}</p>
23
+ </div>
24
+ )
25
+ function handleClick () {
26
+ copy(url)
27
+ }
28
+ return (
29
+ <div className='web-address-bar pd1'>
30
+ <Tooltip
31
+ title={content}
32
+ >
33
+ <Input
34
+ value={url}
35
+ onClick={handleClick}
36
+ addonBefore={
37
+ <ReloadOutlined
38
+ onClick={onReload}
39
+ />
40
+ }
41
+ addonAfter={
42
+ <GlobalOutlined
43
+ onClick={onOpen}
44
+ />
45
+ }
46
+ />
47
+ </Tooltip>
48
+ </div>
49
+ )
50
+ }
@@ -1,19 +1,41 @@
1
- import { useEffect } from 'react'
2
- import Link from '../common/external-link'
1
+ import AddressBar from './address-bar'
3
2
 
4
3
  export default function WebSession (props) {
5
4
  const {
6
- tab
5
+ tab,
6
+ width,
7
+ height,
8
+ reloadTab
7
9
  } = props
8
- useEffect(() => {
9
- tab.url && window.openLink(tab.url)
10
- }, [])
10
+ const addrProps = {
11
+ url: tab.url,
12
+ title: tab.title,
13
+ description: tab.description,
14
+ onOpen: () => {
15
+ window.openLink(tab.url)
16
+ },
17
+ onReload: () => {
18
+ reloadTab(
19
+ tab
20
+ )
21
+ }
22
+ }
23
+ const viewProps = {
24
+ src: tab.url,
25
+ style: {
26
+ width: (width - 10) + 'px',
27
+ height: (height - 12) + 'px'
28
+ },
29
+ disableblinkfeatures: 'true',
30
+ disablewebsecurity: 'true'
31
+ }
11
32
  return (
12
33
  <div className='web-session-wrap'>
13
- <div className='pd3 aligncenter'>
14
- <h1>{tab.title}</h1>
15
- <p>{tab.description}</p>
16
- <Link to={tab.url}>{tab.url}</Link>
34
+ <AddressBar {...addrProps} />
35
+ <div className='pd1'>
36
+ <webview
37
+ {...viewProps}
38
+ />
17
39
  </div>
18
40
  </div>
19
41
  )
@@ -1,6 +1,6 @@
1
- import { render } from 'react-dom'
1
+ import { createRoot } from 'react-dom/client'
2
2
  import 'antd/dist/reset.css'
3
- import 'xterm/css/xterm.css'
3
+ import '@xterm/xterm/css/xterm.css'
4
4
  import '../common/trzsz'
5
5
  import 'firacode/distr/fira_code.css'
6
6
  import Main from '../components/main/index.jsx'
@@ -9,8 +9,7 @@ notification.config({
9
9
  placement: 'bottomRight'
10
10
  })
11
11
 
12
- const rootElement = document.getElementById('container')
13
- render(
14
- <Main />,
15
- rootElement
12
+ const rootElement = createRoot(document.getElementById('container'))
13
+ rootElement.render(
14
+ <Main />
16
15
  )
@@ -9,7 +9,7 @@ import initWatch from './watch'
9
9
  export default (Store) => {
10
10
  Store.prototype.checkForDbUpgrade = async function () {
11
11
  const { store } = window
12
- if (store.isSencondInstance) {
12
+ if (store.isSecondInstance) {
13
13
  return false
14
14
  }
15
15
  const shouldUpgrade = await window.pre.runGlobalAsync('checkDbUpgrade')
@@ -73,7 +73,7 @@ export default Store => {
73
73
  clickCount: ((qm.clickCount || 0) + 1)
74
74
  })
75
75
  }
76
- }, 100)
76
+ }, 200)
77
77
 
78
78
  Store.prototype.setQmSortByFrequency = function (v) {
79
79
  window.store.qmSortByFrequency = v
@@ -190,7 +190,7 @@ export default Store => {
190
190
 
191
191
  Store.prototype.openSettingModal = function () {
192
192
  const { store } = window
193
- if (store.isSencondInstance) {
193
+ if (store.isSecondInstance) {
194
194
  return message.warning(
195
195
  e('sencondInstanceTip')
196
196
  )
@@ -5,11 +5,14 @@
5
5
  import { uniq, debounce, findIndex } from 'lodash-es'
6
6
  import {
7
7
  tabActions,
8
- splitConfig
8
+ splitConfig,
9
+ statusMap,
10
+ paneMap
9
11
  } from '../common/constants'
10
12
  import postMsg from '../common/post-msg'
11
13
  import * as ls from '../common/safe-local-storage'
12
14
  import deepCopy from 'json-deep-copy'
15
+ import generate from '../common/id-with-stamp'
13
16
 
14
17
  export default Store => {
15
18
  Store.prototype.updateTabsStatus = function () {
@@ -63,7 +66,7 @@ export default Store => {
63
66
  postMsg({
64
67
  action: tabActions.addTab,
65
68
  tab,
66
- batch: window.openTabBatch ?? window.store.currentLayoutBatch,
69
+ batch: tab?.batch ?? window.openTabBatch ?? window.store.currentLayoutBatch,
67
70
  index
68
71
  })
69
72
  }
@@ -96,6 +99,28 @@ export default Store => {
96
99
  }
97
100
  }
98
101
 
102
+ Store.prototype.cloneToNextLayout = function () {
103
+ const { store } = window
104
+ const defaultStatus = statusMap.processing
105
+ const { currentTab, layout, currentLayoutBatch } = store
106
+ const ntb = deepCopy(currentTab)
107
+ Object.assign(ntb, {
108
+ id: generate(),
109
+ status: defaultStatus,
110
+ isTransporting: undefined,
111
+ pane: paneMap.terminal
112
+ })
113
+ let maxBatch = splitConfig[layout].children
114
+ if (maxBatch < 2) {
115
+ maxBatch = 2
116
+ }
117
+ ntb.batch = (currentLayoutBatch + 1) % maxBatch
118
+ store.addTab(ntb)
119
+ if (layout === 'c1') {
120
+ store.setLayout('c2')
121
+ }
122
+ }
123
+
99
124
  Store.prototype.setLayout = function (layout) {
100
125
  const {
101
126
  store
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "1.50.21",
3
+ "version": "1.50.31",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",