@electerm/electerm-react 1.60.56 → 1.70.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 (40) hide show
  1. package/client/common/constants.js +1 -0
  2. package/client/common/default-log-path.js +5 -0
  3. package/client/common/default-setting.js +3 -1
  4. package/client/common/find-bookmark-group-id.js +1 -2
  5. package/client/components/batch-op/batch-op-entry.jsx +13 -0
  6. package/client/components/bookmark-form/index.jsx +1 -1
  7. package/client/components/footer/footer-entry.jsx +9 -16
  8. package/client/components/icons/split-view.jsx +14 -0
  9. package/client/components/main/main.jsx +9 -10
  10. package/client/components/session/session.jsx +285 -70
  11. package/client/components/session/session.styl +2 -0
  12. package/client/components/setting-panel/on-tree-drop.js +10 -19
  13. package/client/components/setting-panel/setting-terminal.jsx +94 -20
  14. package/client/components/setting-panel/tab-settings.jsx +2 -1
  15. package/client/components/setting-sync/server-data-status.jsx +81 -0
  16. package/client/components/setting-sync/setting-sync-form.jsx +6 -0
  17. package/client/components/setting-sync/setting-sync.jsx +8 -5
  18. package/client/components/sftp/list-table-ui.jsx +13 -15
  19. package/client/components/sftp/sftp-entry.jsx +4 -22
  20. package/client/components/shortcuts/shortcut-control.jsx +10 -1
  21. package/client/components/tabs/tab.jsx +7 -8
  22. package/client/components/tabs/tabs.styl +3 -0
  23. package/client/components/terminal/term-search.jsx +2 -1
  24. package/client/components/terminal/terminal.jsx +26 -8
  25. package/client/components/terminal-info/base.jsx +9 -4
  26. package/client/components/tree-list/bookmark-toolbar.jsx +2 -3
  27. package/client/components/tree-list/tree-list.jsx +4 -7
  28. package/client/components/tree-list/tree-search.jsx +1 -0
  29. package/client/store/bookmark-group.js +1 -2
  30. package/client/store/init-state.js +3 -0
  31. package/client/store/item.js +1 -2
  32. package/client/store/load-data.js +1 -1
  33. package/client/store/setting.js +1 -2
  34. package/client/store/store.js +13 -15
  35. package/client/store/sync.js +42 -7
  36. package/client/store/terminal-theme.js +4 -4
  37. package/client/store/ui-theme.js +3 -10
  38. package/client/store/watch.js +6 -0
  39. package/package.json +1 -1
  40. package/client/components/main/loading.jsx +0 -25
@@ -3,7 +3,7 @@ import { handleErr } from '../../common/fetch.jsx'
3
3
  import generate from '../../common/uid.js'
4
4
  import { isEqual, pick, debounce, throttle } from 'lodash-es'
5
5
  import clone from '../../common/to-simple-obj.js'
6
- // import runIdle from '../../common/run-idle'
6
+ import resolve from '../../common/resolve.js'
7
7
  import {
8
8
  ReloadOutlined
9
9
  } from '@ant-design/icons'
@@ -51,6 +51,7 @@ import { formatBytes } from '../../common/byte-format.js'
51
51
  import * as fs from './fs.js'
52
52
  import iconsMap from '../sys-menu/icons-map.jsx'
53
53
  import { refs } from '../common/ref.js'
54
+ import createDefaultLogPath from '../../common/default-log-path.js'
54
55
 
55
56
  const e = window.translate
56
57
 
@@ -65,13 +66,13 @@ class Term extends Component {
65
66
  passType: 'password',
66
67
  lines: []
67
68
  }
69
+ this.id = `term-${this.props.tab.id}`
70
+ refs.add(this.id, this)
68
71
  }
69
72
 
70
73
  domRef = createRef()
71
74
 
72
75
  componentDidMount () {
73
- this.id = `term-${this.props.tab.id}`
74
- refs.add(this.id, this)
75
76
  this.initTerminal()
76
77
  if (this.props.tab.enableSsh === false) {
77
78
  ;(
@@ -154,6 +155,9 @@ clear\r`
154
155
 
155
156
  componentWillUnmount () {
156
157
  refs.remove(this.id)
158
+ if (window.store.activeTerminalId === this.props.tab.id) {
159
+ window.store.activeTerminalId = ''
160
+ }
157
161
  if (this.zsession) {
158
162
  this.onZmodemEnd()
159
163
  }
@@ -172,10 +176,6 @@ clear\r`
172
176
  this.term.dispose()
173
177
  this.term = null
174
178
  }
175
- window.removeEventListener(
176
- 'resize',
177
- this.onResize
178
- )
179
179
  this.attachAddon = null
180
180
  this.fitAddon = null
181
181
  this.zmodemAddon = null
@@ -314,7 +314,23 @@ clear\r`
314
314
  }
315
315
 
316
316
  onDrop = e => {
317
- const files = e?.dataTransfer?.files
317
+ const dt = e.dataTransfer
318
+ const fromFile = dt.getData('fromFile')
319
+
320
+ if (fromFile) {
321
+ // Handle SFTP file drop
322
+ try {
323
+ const fileData = JSON.parse(fromFile)
324
+ const filePath = resolve(fileData.path, fileData.name)
325
+ this.attachAddon._sendData(`"${filePath}" `)
326
+ return
327
+ } catch (e) {
328
+ log.error('Failed to parse fromFile data:', e)
329
+ }
330
+ }
331
+
332
+ // Handle regular file drop
333
+ const files = dt.files
318
334
  if (files && files.length) {
319
335
  this.attachAddon._sendData(
320
336
  Array.from(files).map(f => `"${f.path}"`).join(' ')
@@ -1014,6 +1030,7 @@ clear\r`
1014
1030
  ...tab,
1015
1031
  ...extra,
1016
1032
  logName,
1033
+ sessionLogPath: config.sessionLogPath || createDefaultLogPath(),
1017
1034
  ...pick(config, [
1018
1035
  'addTimeStampToTermLog',
1019
1036
  'keepaliveInterval',
@@ -1058,6 +1075,7 @@ clear\r`
1058
1075
  return
1059
1076
  }
1060
1077
  this.setStatus(statusMap.success)
1078
+ refs.get('sftp-' + id)?.initData()
1061
1079
  term.pid = id
1062
1080
  this.pid = id
1063
1081
  const wsUrl = this.buildWsUrl()
@@ -19,6 +19,7 @@ import {
19
19
  ApiOutlined,
20
20
  PartitionOutlined
21
21
  } from '@ant-design/icons'
22
+ import createDefaultSessionLogPath from '../../common/default-log-path'
22
23
  import { refs } from '../common/ref'
23
24
 
24
25
  const e = window.translate
@@ -42,6 +43,10 @@ export default class TerminalInfoBase extends Component {
42
43
  this.getState()
43
44
  }
44
45
 
46
+ componentWillUnmount () {
47
+ clearTimeout(this.timer)
48
+ }
49
+
45
50
  handleToggleTimestamp = () => {
46
51
  const { saveTerminalLogToFile, addTimeStampToTermLog } = this.state
47
52
  const {
@@ -102,6 +107,8 @@ export default class TerminalInfoBase extends Component {
102
107
  saveTerminalLogToFile: term.state.saveTerminalLogToFile,
103
108
  addTimeStampToTermLog: term.state.addTimeStampToTermLog
104
109
  })
110
+ } else {
111
+ this.timer = setTimeout(this.getState, 100)
105
112
  }
106
113
  }
107
114
 
@@ -153,12 +160,10 @@ export default class TerminalInfoBase extends Component {
153
160
  const {
154
161
  id,
155
162
  logName,
156
- appPath
163
+ sessionLogPath
157
164
  } = this.props
158
165
  const { saveTerminalLogToFile } = this.state
159
- const base = appPath
160
- ? osResolve(appPath, 'electerm', 'session_logs')
161
- : window.et.sessionLogPath
166
+ const base = sessionLogPath || createDefaultSessionLogPath()
162
167
  const path = osResolve(base, logName + '.log')
163
168
  const name = e('saveTerminalLogToFile')
164
169
  const to = saveTerminalLogToFile
@@ -10,7 +10,7 @@ import {
10
10
  import { Button, Space, Dropdown, Upload } from 'antd'
11
11
  import copy from 'json-deep-copy'
12
12
  import time from '../../common/time'
13
- import { find, uniq } from 'lodash-es'
13
+ import { uniq } from 'lodash-es'
14
14
  import { fixBookmarks } from '../../common/db-fix'
15
15
  import download from '../../common/download'
16
16
  import { action } from 'manate'
@@ -59,8 +59,7 @@ export default function BookmarkToolbar (props) {
59
59
  if (!bmgTree.has(bg.id)) {
60
60
  store.bookmarkGroups.push(bg)
61
61
  } else {
62
- const bg1 = find(
63
- store.bookmarkGroups,
62
+ const bg1 = store.bookmarkGroups.find(
64
63
  b => b.id === bg.id
65
64
  )
66
65
  bg1.bookmarkIds = uniq(
@@ -10,7 +10,7 @@ import {
10
10
  } from '@ant-design/icons'
11
11
  import createName from '../../common/create-title'
12
12
  import InputAutoFocus from '../common/input-auto-focus'
13
- import { find, uniq, filter, pick } from 'lodash-es'
13
+ import { uniq, filter, pick } from 'lodash-es'
14
14
  import {
15
15
  maxBookmarkGroupTitleLength,
16
16
  defaultBookmarkGroupId,
@@ -146,8 +146,7 @@ export default class ItemListTree extends Component {
146
146
  return
147
147
  }
148
148
  const { bookmarkGroups } = window.store
149
- const obj = find(
150
- bookmarkGroups,
149
+ const obj = bookmarkGroups.find(
151
150
  bg => bg.id === categoryId
152
151
  )
153
152
  if (!obj) {
@@ -229,8 +228,7 @@ export default class ItemListTree extends Component {
229
228
  bookmarkIds: []
230
229
  }
231
230
  bookmarkGroups.unshift(newCat)
232
- const cat = find(
233
- bookmarkGroups,
231
+ const cat = bookmarkGroups.find(
234
232
  d => d.id === id
235
233
  )
236
234
  if (!cat) {
@@ -286,8 +284,7 @@ export default class ItemListTree extends Component {
286
284
  currentBookmarkGroupId: findBookmarkGroupId(store.bookmarkGroups, id)
287
285
  })
288
286
  const { bookmarks } = this.props
289
- const bookmark = find(
290
- bookmarks,
287
+ const bookmark = bookmarks.find(
291
288
  d => d.id === id
292
289
  )
293
290
  if (bookmark) {
@@ -22,6 +22,7 @@ export default memo(function TreeSearchComponent ({ onSearch, keyword }) {
22
22
  <Search
23
23
  onChange={handleChange}
24
24
  value={searchTerm}
25
+ allowClear
25
26
  />
26
27
  )
27
28
  })
@@ -2,7 +2,6 @@
2
2
  * bookmark group functions
3
3
  */
4
4
 
5
- import { find } from 'lodash-es'
6
5
  import {
7
6
  defaultBookmarkGroupId,
8
7
  settingMap
@@ -32,7 +31,7 @@ export default Store => {
32
31
  const gids = item.bookmarkGroupIds || []
33
32
  const bookmarkGroups = store.bookmarkGroups
34
33
  for (const gid of gids) {
35
- const g = find(bookmarkGroups, g => g.id === gid)
34
+ const g = bookmarkGroups.find(g => g.id === gid)
36
35
  if (g && g.bookmarkIds && g.bookmarkIds.length) {
37
36
  ids = [
38
37
  ...ids,
@@ -20,6 +20,7 @@ import {
20
20
  qmSortByFrequencyKey,
21
21
  resolutionsLsKey,
22
22
  aiChatHistoryKey,
23
+ syncServerDataKey,
23
24
  splitMap
24
25
  } from '../common/constants'
25
26
  import { buildDefaultThemes } from '../common/terminal-theme'
@@ -124,6 +125,8 @@ export default () => {
124
125
  isSyncUpload: false,
125
126
  isSyncDownload: false,
126
127
  syncType: syncTypes.github,
128
+ // syncServerData: {},
129
+ syncServerStatus: ls.getItemJSON(syncServerDataKey, {}),
127
130
 
128
131
  // term search
129
132
  termSearchOpen: false,
@@ -2,7 +2,6 @@
2
2
  * common db op
3
3
  */
4
4
 
5
- import { find } from 'lodash-es'
6
5
  import deepCopy from 'json-deep-copy'
7
6
  import {
8
7
  settingMap
@@ -23,7 +22,7 @@ export default Store => {
23
22
  Store.prototype.editItem = function (id, updates, type) {
24
23
  const { store } = window
25
24
  const items = store.getItems(type)
26
- const item = find(items, t => t.id === id)
25
+ const item = items.find(t => t.id === id)
27
26
  if (!item) {
28
27
  return
29
28
  }
@@ -115,7 +115,7 @@ export default (Store) => {
115
115
  if (!arr.length && store.config.initDefaultTabOnStart) {
116
116
  store.initFirstTab()
117
117
  }
118
- setTimeout(store.confirmLoad, 1300)
118
+ store.confirmLoad()
119
119
  const { initTime, loadTime } = window.pre.runSync('getLoadTime')
120
120
  if (loadTime) {
121
121
  store.loadTime = loadTime
@@ -2,7 +2,6 @@
2
2
  * setting modal
3
3
  */
4
4
 
5
- import { find } from 'lodash-es'
6
5
  import {
7
6
  message
8
7
  } from 'antd'
@@ -66,7 +65,7 @@ export default Store => {
66
65
  const { store } = window
67
66
  const bookmarks = store.bookmarks
68
67
  const item = copy(
69
- find(bookmarks, it => it.id === id)
68
+ bookmarks.find(it => it.id === id)
70
69
  )
71
70
  if (!item) {
72
71
  return
@@ -31,17 +31,18 @@ import deepCopy from 'json-deep-copy'
31
31
  import getBrand from '../components/ai/get-brand'
32
32
  import {
33
33
  settingMap,
34
- paneMap,
35
34
  settingSyncId,
36
35
  settingShortcutsId,
37
36
  settingTerminalId,
38
- terminalSshConfigType
37
+ terminalSshConfigType,
38
+ paneMap
39
39
  } from '../common/constants'
40
40
  import getInitItem from '../common/init-setting-item'
41
41
  import createTitle from '../common/create-title'
42
42
  import {
43
43
  theme
44
44
  } from 'antd'
45
+ import { refs } from '../components/common/ref'
45
46
 
46
47
  const e = window.translate
47
48
 
@@ -65,7 +66,7 @@ class Store {
65
66
  const { currentTab } = this
66
67
  const { quickCommands } = window.store
67
68
  const currentTabQuickCommands = (
68
- currentTab.quickCommands || []
69
+ currentTab?.quickCommands || []
69
70
  ).map((d, i) => {
70
71
  return {
71
72
  ...d,
@@ -82,12 +83,11 @@ class Store {
82
83
  const {
83
84
  activeTabId
84
85
  } = this
85
- const { tabs } = window.store
86
- const tab = tabs.find(t => t.id === activeTabId)
86
+ const tab = refs.get('tab-' + activeTabId)
87
87
  if (!tab) {
88
- return false
88
+ return null
89
89
  }
90
- return tab
90
+ return tab.props.tab
91
91
  }
92
92
 
93
93
  get batchInputSelectedTabIds () {
@@ -112,21 +112,19 @@ class Store {
112
112
  if (store.showModal) {
113
113
  return false
114
114
  }
115
- const {
116
- currentTab
117
- } = store
115
+ const { currentTab } = store
118
116
  if (!currentTab) {
119
117
  return false
120
118
  }
121
119
  const {
122
- type,
123
- pane
120
+ type
124
121
  } = currentTab
125
- if (type === 'rdp' || type === 'vnc' || type === 'web') {
122
+ if (type === 'web' || type === 'rdp' || type === 'vnc') {
126
123
  return false
127
124
  }
128
- return pane === paneMap.ssh ||
129
- pane === paneMap.terminal
125
+ return currentTab.sshSftpSplitView ||
126
+ currentTab.pane === paneMap.terminal ||
127
+ currentTab.pane === paneMap.ssh
130
128
  }
131
129
 
132
130
  get quickCommandTags () {
@@ -34,6 +34,13 @@ async function fetchData (type, func, args, token, proxy) {
34
34
  return fetch(data)
35
35
  }
36
36
 
37
+ function updateSyncServerStatusFromGist (store, gist, type) {
38
+ const status = parseJsonSafe(
39
+ get(gist, 'files["electerm-status.json"].content')
40
+ )
41
+ store.syncServerStatus[type] = status
42
+ }
43
+
37
44
  export default (Store) => {
38
45
  Store.prototype.updateSyncSetting = function (data) {
39
46
  const { store } = window
@@ -167,6 +174,20 @@ export default (Store) => {
167
174
  window[type + 'IsSyncing'] = false
168
175
  }
169
176
 
177
+ Store.prototype.previewServerData = async function (type) {
178
+ const { store } = window
179
+ const token = store.getSyncToken(type)
180
+ const gistId = store.getSyncGistId(type)
181
+ const gist = await fetchData(
182
+ type,
183
+ 'getOne',
184
+ [gistId],
185
+ token,
186
+ store.getProxySetting()
187
+ )
188
+ updateSyncServerStatusFromGist(store, gist, type)
189
+ }
190
+
170
191
  Store.prototype.uploadSettingAction = async function (type) {
171
192
  const { store } = window
172
193
  const token = store.getSyncToken(type)
@@ -212,22 +233,33 @@ export default (Store) => {
212
233
  }
213
234
  }
214
235
  }
215
- const res = await fetchData(type, 'update', [gistId, {
236
+ const now = Date.now()
237
+ const status = {
238
+ lastSyncTime: now,
239
+ electermVersion: packVer,
240
+ deviceName: window.pre.osInfo().find(r => r.k === 'hostname')?.v || 'unknown'
241
+ }
242
+ const gistData = {
216
243
  description: 'sync electerm data',
217
244
  files: {
218
245
  ...objs,
219
246
  'electerm-status.json': {
220
- content: JSON.stringify({
221
- lastSyncTime: Date.now(),
222
- electermVersion: packVer
223
- })
247
+ content: JSON.stringify(status)
224
248
  }
225
249
  }
226
- }], token, store.getProxySetting())
250
+ }
251
+ const res = await fetchData(
252
+ type,
253
+ 'update',
254
+ [gistId, gistData],
255
+ token,
256
+ store.getProxySetting()
257
+ )
227
258
  if (res) {
228
259
  store.updateSyncSetting({
229
- [type + 'LastSyncTime']: Date.now()
260
+ [type + 'LastSyncTime']: now
230
261
  })
262
+ updateSyncServerStatusFromGist(store, gistData, type)
231
263
  }
232
264
  }
233
265
 
@@ -259,6 +291,9 @@ export default (Store) => {
259
291
  token,
260
292
  store.getProxySetting()
261
293
  )
294
+ if (gist) {
295
+ updateSyncServerStatusFromGist(store, gist, type)
296
+ }
262
297
  const { names, syncConfig } = store.getDataSyncNames()
263
298
  for (const n of names) {
264
299
  let str = get(gist, `files["${n}.json"].content`)
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import { message } from 'antd'
6
- import { find, isEqual } from 'lodash-es'
6
+ import { isEqual } from 'lodash-es'
7
7
  import {
8
8
  defaultTheme,
9
9
  settingMap,
@@ -42,7 +42,7 @@ export default Store => {
42
42
  Store.prototype.getThemeConfig = function () {
43
43
  const { store } = window
44
44
  const all = store.getSidebarList(settingMap.terminalThemes)
45
- return (find(all, d => d.id === store.config.theme) || {}).themeConfig || {}
45
+ return (all.find(d => d.id === store.config.theme) || {}).themeConfig || {}
46
46
  }
47
47
 
48
48
  Store.prototype.fixThemes = function (themes) {
@@ -82,7 +82,7 @@ export default Store => {
82
82
  Store.prototype.checkDefaultTheme = async function (terminalThemes) {
83
83
  const { store } = window
84
84
  const themeId = defaultTheme.id
85
- const currentDefaultTheme = find(store.terminalThemes, d => d.id === themeId)
85
+ const currentDefaultTheme = store.terminalThemes.find(d => d.id === themeId)
86
86
  if (
87
87
  currentDefaultTheme &&
88
88
  (
@@ -102,7 +102,7 @@ export default Store => {
102
102
  `${e('default')} ${e('themeConfig')} ${e('updated')}`
103
103
  )
104
104
  }
105
- const hasLightTheme = find(store.getTerminalThemes(), d => d.id === defaultThemeLight.id)
105
+ const hasLightTheme = store.getTerminalThemes().find(d => d.id === defaultThemeLight.id)
106
106
  if (!hasLightTheme) {
107
107
  store.addTheme(defaultThemeLight)
108
108
  }
@@ -2,16 +2,11 @@
2
2
  * ui theme functions
3
3
  */
4
4
 
5
- /**
6
- * theme related functions
7
- */
8
-
9
- import { escapeRegExp, find } from 'lodash-es'
5
+ import { escapeRegExp } from 'lodash-es'
10
6
  import {
11
7
  defaultTheme,
12
8
  settingMap
13
9
  } from '../common/constants'
14
- // import fetch from '../common/fetch'
15
10
  import copy from 'json-deep-copy'
16
11
 
17
12
  export default Store => {
@@ -51,10 +46,8 @@ export default Store => {
51
46
 
52
47
  Store.prototype.getUiThemeConfig = function () {
53
48
  const { store } = window
54
- const theme = find(
55
- store.getSidebarList(settingMap.terminalThemes),
56
- d => d.id === store.config.theme
57
- )
49
+ const theme = store.getSidebarList(settingMap.terminalThemes)
50
+ .find(d => d.id === store.config.theme)
58
51
  return theme && theme.uiThemeConfig
59
52
  ? copy(theme.uiThemeConfig)
60
53
  : defaultTheme.uiThemeConfig
@@ -11,6 +11,7 @@ import {
11
11
  expandedKeysLsKey,
12
12
  resolutionsLsKey,
13
13
  localAddrBookmarkLsKey,
14
+ syncServerDataKey,
14
15
  aiChatHistoryKey
15
16
  } from '../common/constants'
16
17
  import * as ls from '../common/safe-local-storage'
@@ -122,6 +123,11 @@ export default store => {
122
123
  return store.checkedKeys
123
124
  }).start()
124
125
 
126
+ autoRun(() => {
127
+ ls.setItemJSON(syncServerDataKey, store.syncServerStatus)
128
+ return store.syncServerStatus
129
+ }).start()
130
+
125
131
  autoRun(() => {
126
132
  ls.setItemJSON('history', store.history)
127
133
  return store.history
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "1.60.56",
3
+ "version": "1.70.0",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",
@@ -1,25 +0,0 @@
1
- import LogoElem from '../common/logo-elem'
2
- import { PureComponent } from 'react'
3
-
4
- export class LoadingUI extends PureComponent {
5
- state = {
6
- show: true
7
- }
8
-
9
- hide = () => {
10
- this.setState({
11
- show: false
12
- })
13
- }
14
-
15
- render () {
16
- if (!this.state.show) {
17
- return null
18
- }
19
- return (
20
- <div className='loading-data'>
21
- <LogoElem />
22
- </div>
23
- )
24
- }
25
- }