@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
@@ -2,73 +2,311 @@
2
2
  * tabs related functions
3
3
  */
4
4
 
5
- import { uniq, debounce, findIndex } from 'lodash-es'
5
+ import { debounce } from 'lodash-es'
6
6
  import {
7
- tabActions,
8
7
  splitConfig,
9
8
  statusMap,
10
9
  paneMap
11
10
  } from '../common/constants'
12
- import postMsg from '../common/post-msg'
13
11
  import * as ls from '../common/safe-local-storage'
14
12
  import deepCopy from 'json-deep-copy'
15
13
  import generate from '../common/id-with-stamp'
14
+ import newTerm from '../common/new-terminal.js'
16
15
 
17
16
  export default Store => {
17
+ Store.prototype.getTabs = function () {
18
+ return window.store.tabs
19
+ }
20
+
21
+ Store.prototype.setOffline = function () {
22
+ const { store } = window
23
+ store.tabs.forEach(tab => {
24
+ if (tab.host && tab.status !== statusMap.error) {
25
+ tab.status = statusMap.error
26
+ }
27
+ })
28
+ }
29
+
18
30
  Store.prototype.updateTabsStatus = function () {
19
- const tabIds = uniq(
20
- window.store.fileTransfers.map(d => d.tabId)
31
+ const { store } = window
32
+ const tabIdSet = new Set(
33
+ store.fileTransfers.map(d => d.tabId)
21
34
  )
22
- postMsg({
23
- action: tabActions.updateTabsStatus,
24
- tabIds
35
+ store.tabs.forEach(tab => {
36
+ tab.isTransporting = tabIdSet.has(tab.id)
25
37
  })
26
38
  }
27
39
 
28
- Store.prototype.updateStoreTabs = function (tabs0, batch0) {
29
- if (!tabs0.length && batch0 !== undefined) {
30
- const tabs = window.store.getTabs().filter(t => t.batch !== batch0)
31
- window.store.setTabs(tabs)
32
- return true
40
+ Store.prototype.setTabs = function (list) {
41
+ window.store.tabs = list
42
+ }
43
+
44
+ Store.prototype.delTab = function (id) {
45
+ return window.store.removeTabs({ id })
46
+ }
47
+
48
+ Store.prototype.closeTabsRight = function (id) {
49
+ const { store } = window
50
+ const { tabs } = store
51
+ const targetTab = tabs.find(t => t.id === id)
52
+ if (!targetTab) {
53
+ return
33
54
  }
34
- if (!tabs0.length) {
35
- return false
55
+ const targetBatch = targetTab.batch
56
+ const targetIndex = tabs.findIndex(t => t.id === id)
57
+
58
+ // Remove all tabs in the same batch that are to the right
59
+ for (let i = tabs.length - 1; i > targetIndex; i--) {
60
+ const closingTab = tabs[i]
61
+ if (closingTab.batch === targetBatch) {
62
+ // Handle current tab closure
63
+ if (closingTab.id === store.currentTabId) {
64
+ store.currentTabId = id
65
+ } else if (closingTab.id === store[`currentTabId${targetBatch}`]) {
66
+ store[`currentTabId${targetBatch}`] = id
67
+ }
68
+
69
+ tabs.splice(i, 1)
70
+ }
36
71
  }
37
- const { batch } = tabs0[0]
38
- const tabs = window.store.getTabs()
39
- .filter(t => t.batch !== batch)
40
- .concat(deepCopy(tabs0))
41
- window.store.setTabs(tabs)
42
72
  }
43
73
 
44
- Store.prototype.getTabs = function () {
45
- return window.store.getItems('tabs')
74
+ Store.prototype.reloadTab = function (tabId = window.store.currentTabId) {
75
+ const { store } = window
76
+ const { tabs } = store
77
+ const index = tabs.findIndex(t => t.id === tabId)
78
+
79
+ // If tab not found, do nothing
80
+ if (index === -1) {
81
+ return
82
+ }
83
+
84
+ const oldTab = tabs[index]
85
+
86
+ // Create copy of old tab with new ID
87
+ const newTab = {
88
+ ...oldTab,
89
+ id: generate(), // Need to create new ID
90
+ status: statusMap.processing // Reset status
91
+ }
92
+
93
+ // Add new tab at next index
94
+ tabs.splice(index + 1, 0, newTab)
95
+
96
+ // Remove old tab
97
+ tabs.splice(index, 1)
98
+
99
+ // Update current tab ID if needed
100
+ if (store.currentTabId === tabId) {
101
+ store.currentTabId = newTab.id
102
+ }
103
+
104
+ // Update batch current tab ID if needed
105
+ const batchProp = `currentTabId${oldTab.batch}`
106
+ if (store[batchProp] === tabId) {
107
+ store[batchProp] = newTab.id
108
+ }
46
109
  }
47
110
 
48
- Store.prototype.setTabs = function (list) {
49
- return window.store.setItems('tabs', list)
111
+ Store.prototype.duplicateTab = function (tabId) {
112
+ const { store } = window
113
+ const { tabs } = store
114
+
115
+ // Find the target tab and its index
116
+ const targetIndex = tabs.findIndex(t => t.id === tabId)
117
+ if (targetIndex === -1) {
118
+ return
119
+ }
120
+
121
+ const sourceTab = tabs[targetIndex]
122
+ const duplicatedTab = {
123
+ ...deepCopy(sourceTab),
124
+ id: generate(),
125
+ status: statusMap.processing,
126
+ isTransporting: undefined
127
+ }
128
+
129
+ // Insert the duplicated tab after the source tab
130
+ tabs.splice(targetIndex + 1, 0, duplicatedTab)
131
+
132
+ // Set the duplicated tab as current
133
+ store.currentTabId = duplicatedTab.id
134
+ store[`currentTabId${sourceTab.batch}`] = duplicatedTab.id
50
135
  }
51
136
 
52
- Store.prototype.delTab = function (id) {
53
- return window.store.delItem({ id }, 'tabs')
137
+ Store.prototype.closeOtherTabs = function (id) {
138
+ const { store } = window
139
+ const { tabs } = store
140
+ const targetTab = tabs.find(t => t.id === id)
141
+ if (!targetTab) {
142
+ return
143
+ }
144
+ const currentBatch = targetTab.batch
145
+
146
+ for (let i = tabs.length - 1; i >= 0; i--) {
147
+ const tab = tabs[i]
148
+ if (tab.batch === currentBatch && tab.id !== id) {
149
+ if (tab.id === store.currentTabId) {
150
+ store.currentTabId = id
151
+ }
152
+ if (tab.id === store[`currentTabId${currentBatch}`]) {
153
+ store[`currentTabId${currentBatch}`] = id
154
+ }
155
+ tabs.splice(i, 1)
156
+ }
157
+ }
54
158
  }
55
159
 
56
- Store.prototype.initFirstTab = function () {
57
- postMsg({
58
- action: tabActions.initFirstTab
59
- })
160
+ Store.prototype.removeTabs = function (condition) {
161
+ const { tabs } = window.store
162
+ const removedIds = []
163
+
164
+ if (typeof condition === 'function') {
165
+ for (let i = tabs.length - 1; i >= 0; i--) {
166
+ const tab = tabs[i]
167
+ if (condition(tab, i)) {
168
+ removedIds.push(tab.id)
169
+ tabs.splice(i, 1)
170
+ }
171
+ }
172
+ } else {
173
+ const keys = Object.keys(condition)
174
+ if (keys.length === 1) {
175
+ const key = keys[0]
176
+ const value = condition[key]
177
+ for (let i = tabs.length - 1; i >= 0; i--) {
178
+ const tab = tabs[i]
179
+ if (tab[key] === value) {
180
+ removedIds.push(tab.id)
181
+ tabs.splice(i, 1)
182
+ }
183
+ }
184
+ } else {
185
+ for (let i = tabs.length - 1; i >= 0; i--) {
186
+ let match = true
187
+ const tab = tabs[i]
188
+ for (const key in condition) {
189
+ if (tab[key] !== condition[key]) {
190
+ match = false
191
+ break
192
+ }
193
+ }
194
+ if (match) {
195
+ removedIds.push(tab.id)
196
+ tabs.splice(i, 1)
197
+ }
198
+ }
199
+ }
200
+ }
201
+
202
+ if (removedIds.length) {
203
+ window.store.fixCurrentTabIds(tabs, removedIds)
204
+ }
205
+ }
206
+
207
+ Store.prototype.fixCurrentTabIds = function (remainingTabs, removedIds) {
208
+ const store = window.store
209
+ const removedSet = new Set(removedIds)
210
+ const batchFirstTabs = {}
211
+ const currentIdNeedFix = removedSet.has(store.currentTabId)
212
+
213
+ // Get first valid tab for each batch
214
+ for (const tab of remainingTabs) {
215
+ if (!batchFirstTabs[tab.batch]) {
216
+ batchFirstTabs[tab.batch] = tab.id
217
+ }
218
+ }
219
+
220
+ // If current tab was removed, we need to set a new one
221
+ if (currentIdNeedFix) {
222
+ // Try to find current batch's first tab
223
+ const currentTab = remainingTabs.find(t => t.id === store.currentTabId)
224
+ const currentBatch = currentTab ? currentTab.batch : store.currentLayoutBatch
225
+ const newCurrentId = batchFirstTabs[currentBatch] || batchFirstTabs[0] || ''
226
+
227
+ if (newCurrentId) {
228
+ store.currentTabId = newCurrentId
229
+ // Also update the batch-specific current tab id
230
+ store[`currentTabId${currentBatch}`] = newCurrentId
231
+ } else {
232
+ // No tabs left in any batch
233
+ store.currentTabId = ''
234
+ }
235
+ }
236
+
237
+ // Fix batch-specific current tab IDs
238
+ for (const batch in batchFirstTabs) {
239
+ const batchTabId = `currentTabId${batch}`
240
+ const currentBatchId = store[batchTabId]
241
+
242
+ // If the batch's current tab was removed or doesn't exist
243
+ if (removedSet.has(currentBatchId) || !currentBatchId) {
244
+ store[batchTabId] = batchFirstTabs[batch]
245
+ }
246
+ }
247
+ store.focus()
248
+ }
249
+
250
+ Store.prototype.initFirstTab = function (batch) {
251
+ const { store } = window
252
+ if (batch !== undefined) {
253
+ const newTab = newTerm()
254
+ newTab.batch = batch // Set batch number
255
+ store.addTab(newTab)
256
+ return
257
+ }
258
+ const { layout } = store
259
+ const batchCount = splitConfig[layout].children || 1
260
+ for (let i = 0; i < batchCount; i++) {
261
+ const newTab = newTerm()
262
+ newTab.batch = i // Set batch number
263
+ store.addTab(newTab)
264
+ }
265
+ }
266
+
267
+ Store.prototype.updateTab = function (id, update) {
268
+ const { store } = window
269
+ const { tabs } = store
270
+
271
+ // Find the target tab
272
+ const targetIndex = tabs.findIndex(t => t.id === id)
273
+ if (targetIndex === -1) {
274
+ return
275
+ }
276
+ // Directly update the tab properties
277
+ Object.assign(tabs[targetIndex], update)
278
+ }
279
+
280
+ Store.prototype.clickTab = function (id, batch) {
281
+ const { store } = window
282
+
283
+ // Update current batch
284
+ store.currentLayoutBatch = batch
285
+
286
+ // Update current tab id
287
+ store.currentTabId = id
288
+
289
+ // Update batch-specific current tab id
290
+ store[`currentTabId${batch}`] = id
60
291
  }
61
292
 
62
293
  Store.prototype.addTab = function (
63
- tab,
64
- index
294
+ newTab = newTerm(),
295
+ index,
296
+ batch
65
297
  ) {
66
- postMsg({
67
- action: tabActions.addTab,
68
- tab,
69
- batch: tab?.batch ?? window.openTabBatch ?? window.store.currentLayoutBatch,
70
- index
71
- })
298
+ const { store } = window
299
+ const { tabs } = store
300
+ newTab.batch = batch ?? newTab.batch ?? window.openTabBatch ?? window.store.currentLayoutBatch
301
+ if (typeof index === 'number' && index >= 0 && index <= tabs.length) {
302
+ tabs.splice(index, 0, newTab)
303
+ } else {
304
+ tabs.push(newTab)
305
+ }
306
+ const batchNum = newTab.batch
307
+ store[`currentTabId${batchNum}`] = newTab.id
308
+ store.currentTabId = newTab.id
309
+ store.currentLayoutBatch = batchNum
72
310
  }
73
311
 
74
312
  Store.prototype.clickNextTab = debounce(function () {
@@ -80,30 +318,44 @@ export default Store => {
80
318
  }, 100)
81
319
 
82
320
  Store.prototype.clickBioTab = function (diff) {
83
- const tab = document.querySelector('.tabs-wrapper .tab.active')
84
- if (tab) {
85
- const id = tab.dataset.id
86
- const { tabs } = window.store
87
- const i = findIndex(tabs, t => {
88
- return t.id === id
89
- })
90
- const len = tabs.length
91
- if (i >= 0) {
92
- const next = (i + diff + len) % len
93
- const nextTab = tabs[next]
94
- postMsg({
95
- action: tabActions.changeCurrentTabId,
96
- currentTabId: nextTab.id
97
- })
98
- }
321
+ const { store } = window
322
+ const { tabs, currentTabId } = store
323
+
324
+ // Find the current tab index and its batch
325
+ const currentIndex = tabs.findIndex(t => t.id === currentTabId)
326
+ if (currentIndex === -1) return // Current tab not found, do nothing
327
+
328
+ const currentBatch = tabs[currentIndex].batch
329
+
330
+ // Function to find the next valid tab index
331
+ const findNextTabIndex = (startIndex, direction) => {
332
+ let nextIndex = startIndex
333
+ do {
334
+ nextIndex = (nextIndex + direction + tabs.length) % tabs.length
335
+ if (tabs[nextIndex].batch === currentBatch) {
336
+ return nextIndex
337
+ }
338
+ } while (nextIndex !== startIndex)
339
+ return -1 // No other tabs in the same batch
340
+ }
341
+
342
+ // Find the next tab index
343
+ const nextIndex = findNextTabIndex(currentIndex, diff)
344
+
345
+ // If a valid next tab is found, update the currentTabId
346
+ if (nextIndex !== -1 && nextIndex !== currentIndex) {
347
+ store.currentTabId = tabs[nextIndex].id
99
348
  }
100
349
  }
101
350
 
102
- Store.prototype.cloneToNextLayout = function () {
351
+ Store.prototype.cloneToNextLayout = function (tab = window.store.currentTab) {
352
+ if (!tab) {
353
+ return
354
+ }
103
355
  const { store } = window
104
356
  const defaultStatus = statusMap.processing
105
- const { currentTab, layout, currentLayoutBatch } = store
106
- const ntb = deepCopy(currentTab)
357
+ const { layout, currentLayoutBatch } = store
358
+ const ntb = deepCopy(tab)
107
359
  Object.assign(ntb, {
108
360
  id: generate(),
109
361
  status: defaultStatus,
@@ -115,50 +367,50 @@ export default Store => {
115
367
  maxBatch = 2
116
368
  }
117
369
  ntb.batch = (currentLayoutBatch + 1) % maxBatch
118
- store.addTab(ntb)
119
370
  if (layout === 'c1') {
120
371
  store.setLayout('c2')
121
372
  }
373
+ store.addTab(ntb)
122
374
  }
123
375
 
124
376
  Store.prototype.setLayout = function (layout) {
125
- const {
126
- store
127
- } = window
377
+ const { store } = window
128
378
  const prevLayout = store.layout
379
+ const { currentTabId } = store
380
+
381
+ // If layout hasn't changed, do nothing
129
382
  if (prevLayout === layout) {
130
383
  return
131
384
  }
385
+
386
+ // Update layout related properties
132
387
  store.prevLayout = prevLayout
133
388
  ls.setItem('layout', layout)
134
389
  store.layout = layout
135
- const len = splitConfig[layout].children
136
- const prevLen = prevLayout ? splitConfig[prevLayout].children : 0
137
- if (len < prevLen) {
138
- const {
139
- tabs
140
- } = store
141
- // Update tabs where batch > len - 1
142
- const updatedTabs = tabs.map(tab => {
143
- if (tab.batch > len - 1) {
144
- return { ...tab, batch: len - 1 }
390
+
391
+ // Get the number of batches in new and previous layouts
392
+ const newBatchCount = splitConfig[layout].children
393
+ const prevBatchCount = prevLayout ? splitConfig[prevLayout].children : 0
394
+
395
+ // If new layout has fewer batches, we need to adjust tabs
396
+ if (newBatchCount < prevBatchCount) {
397
+ const nb = newBatchCount - 1
398
+ // Directly modify tabs that exceed the new batch count
399
+ for (let i = 0; i < store.tabs.length; i++) {
400
+ const tab = store.tabs[i]
401
+ if (tab.batch >= newBatchCount) {
402
+ store.tabs[i].batch = nb
403
+ if (tab.id === currentTabId) {
404
+ store[`currentTabId${nb}`] = currentTabId
405
+ }
145
406
  }
146
- return tab
147
- })
148
- // Set the updated tabs back to the store
149
- store.setTabs(updatedTabs)
150
- setTimeout(
151
- () => {
152
- postMsg({
153
- action: tabActions.changeCurrentTabId,
154
- currentTabId: store.currentTabId
155
- })
156
- },
157
- 1000
158
- )
159
- if (store.currentLayoutBatch > len - 1) {
160
- store.currentLayoutBatch = len - 1
407
+ }
408
+
409
+ // Adjust currentLayoutBatch if needed
410
+ if (store.currentLayoutBatch >= newBatchCount) {
411
+ store.currentLayoutBatch = newBatchCount - 1
161
412
  }
162
413
  }
414
+ store.focus()
163
415
  }
164
416
  }
@@ -6,14 +6,12 @@ import createTitle from '../common/create-title'
6
6
  import { autoRun } from 'manate'
7
7
  import { update, dbNamesForWatch } from '../common/db'
8
8
  import {
9
- commonActions,
10
9
  sftpDefaultSortSettingKey,
11
10
  checkedKeysLsKey,
12
11
  expandedKeysLsKey,
13
12
  resolutionsLsKey,
14
13
  localAddrBookmarkLsKey
15
14
  } from '../common/constants'
16
- import postMsg from '../common/post-msg'
17
15
  import * as ls from '../common/safe-local-storage'
18
16
  import { debounce, isEmpty } from 'lodash-es'
19
17
 
@@ -106,11 +104,8 @@ export default store => {
106
104
  if (tab) {
107
105
  const title = createTitle(tab)
108
106
  window.pre.runGlobalAsync('setTitle', title)
107
+ window.store.currentLayoutBatch = tab.batch
109
108
  }
110
- postMsg({
111
- action: commonActions.changeCurrentTabId,
112
- currentTabId
113
- })
114
109
  return store.currentTabId
115
110
  }).start()
116
111
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "1.50.66",
3
+ "version": "1.51.1",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",