@electerm/electerm-react 1.51.21 → 1.60.16

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 (39) hide show
  1. package/client/common/constants.js +2 -0
  2. package/client/common/default-setting.js +12 -1
  3. package/client/components/ai/ai-chat-history-item.jsx +69 -0
  4. package/client/components/ai/ai-chat-history.jsx +31 -0
  5. package/client/components/ai/ai-chat.jsx +172 -0
  6. package/client/components/ai/ai-config.jsx +145 -0
  7. package/client/components/ai/ai-output.jsx +118 -0
  8. package/client/components/ai/ai.styl +70 -0
  9. package/client/components/ai/get-brand.js +34 -0
  10. package/client/components/ai/providers.js +14 -0
  11. package/client/components/bookmark-form/rdp-form-ui.jsx +1 -1
  12. package/client/components/footer/batch-input.jsx +13 -67
  13. package/client/components/footer/footer-entry.jsx +19 -3
  14. package/client/components/footer/footer.styl +4 -0
  15. package/client/components/footer/tab-select.jsx +9 -3
  16. package/client/components/layout/layout.jsx +5 -4
  17. package/client/components/main/main.jsx +20 -4
  18. package/client/components/shortcuts/shortcut-control.jsx +17 -2
  19. package/client/components/shortcuts/shortcut-handler.js +24 -8
  20. package/client/components/shortcuts/shortcuts-defaults.js +6 -0
  21. package/client/components/side-panel-r/right-side-panel.styl +6 -7
  22. package/client/components/side-panel-r/side-panel-r.jsx +32 -10
  23. package/client/components/sidebar/app-running-time.jsx +35 -0
  24. package/client/components/sidebar/history-item.jsx +20 -3
  25. package/client/components/sidebar/history.jsx +4 -1
  26. package/client/components/sidebar/info-modal.jsx +2 -0
  27. package/client/components/tabs/app-drag.jsx +1 -1
  28. package/client/components/tabs/index.jsx +16 -43
  29. package/client/components/tabs/no-session.jsx +40 -0
  30. package/client/components/tabs/tabs.styl +6 -0
  31. package/client/components/terminal/index.jsx +2 -2
  32. package/client/store/common.js +37 -2
  33. package/client/store/index.js +2 -290
  34. package/client/store/init-state.js +7 -1
  35. package/client/store/store.js +313 -0
  36. package/client/store/sync.js +4 -1
  37. package/client/store/tab.js +56 -2
  38. package/client/store/watch.js +9 -2
  39. package/package.json +1 -1
@@ -4,7 +4,7 @@
4
4
 
5
5
  import React from 'react'
6
6
  import runIdle from '../../common/run-idle'
7
- import { debounce } from 'lodash-es'
7
+ import { throttle } from 'lodash-es'
8
8
  import TabTitle from './tab-title'
9
9
  import {
10
10
  CodeFilled,
@@ -24,9 +24,8 @@ import {
24
24
  TwoRowsRightIcon,
25
25
  TwoColumnsBottomIcon
26
26
  } from '../icons/split-icons'
27
- import { Dropdown, Popover, Button } from 'antd'
27
+ import { Dropdown, Popover } from 'antd'
28
28
  import Tab from './tab'
29
- import LogoElem from '../common/logo-elem.jsx'
30
29
  import './tabs.styl'
31
30
  import {
32
31
  tabWidth,
@@ -41,6 +40,7 @@ import findParentBySel from '../../common/find-parent'
41
40
  import WindowControl from './window-control'
42
41
  import BookmarksList from '../sidebar/bookmark-select'
43
42
  import AppDrag from './app-drag'
43
+ import NoSession from './no-session'
44
44
  import classNames from 'classnames'
45
45
 
46
46
  const e = window.translate
@@ -49,6 +49,7 @@ export default class Tabs extends React.Component {
49
49
  constructor (props) {
50
50
  super(props)
51
51
  this.tabsRef = React.createRef()
52
+ this.domRef = React.createRef()
52
53
  this.state = {
53
54
  overflow: false,
54
55
  receiveDataTabId: '',
@@ -60,13 +61,13 @@ export default class Tabs extends React.Component {
60
61
  }
61
62
 
62
63
  componentDidMount () {
63
- this.domRef = React.createRef()
64
64
  const {
65
65
  tabsRef
66
66
  } = this
67
67
  window.addEventListener('message', this.onEvent)
68
- tabsRef.current.addEventListener('mousedown', this.handleClickEvent)
69
- tabsRef.current.addEventListener('mousewheel', this.handleWheelEvent)
68
+ tabsRef.current.addEventListener('wheel', this.handleWheelEvent, {
69
+ passive: false
70
+ })
70
71
  }
71
72
 
72
73
  componentDidUpdate (prevProps) {
@@ -198,16 +199,6 @@ export default class Tabs extends React.Component {
198
199
  return inner ? inner.clientWidth : 0
199
200
  }
200
201
 
201
- handleClickEvent = (e) => {
202
- if (e.button === 1) {
203
- const p = findParentBySel(e.target, '.tab')
204
- if (p) {
205
- const id = p.dataset.id
206
- this.props.delTab(id)
207
- }
208
- }
209
- }
210
-
211
202
  handleAdd = e => {
212
203
  if (!e.target.className.includes('tabs-wrapper')) {
213
204
  return
@@ -230,7 +221,7 @@ export default class Tabs extends React.Component {
230
221
  const index = tabs.findIndex(t => t.id === currentBatchTabId)
231
222
  const tabsDomWith = Array.from(
232
223
  document.querySelectorAll(`.v${batch + 1} .tab`)
233
- ).slice(0, index + 1).reduce((prev, c) => {
224
+ ).slice(0, index + 2).reduce((prev, c) => {
234
225
  return prev + c.clientWidth
235
226
  }, 0)
236
227
  const w = (index + 1) * tabMargin + tabsDomWith
@@ -262,7 +253,7 @@ export default class Tabs extends React.Component {
262
253
  this.domRef.current.scrollTo({ left: scrollLeft, behavior: 'smooth' })
263
254
  }
264
255
 
265
- handleWheelEvent = debounce((e) => {
256
+ handleWheelEvent = throttle((e) => {
266
257
  if (this.isOverflow()) {
267
258
  if (e.deltaY < 0) {
268
259
  this.handleScrollLeft()
@@ -270,7 +261,7 @@ export default class Tabs extends React.Component {
270
261
  this.handleScrollRight()
271
262
  }
272
263
  }
273
- }, 100)
264
+ }, 100, { leading: true, trailing: true })
274
265
 
275
266
  handleClickMenu = ({ key }) => {
276
267
  const id = key.split('##')[1]
@@ -557,31 +548,13 @@ export default class Tabs extends React.Component {
557
548
  }
558
549
 
559
550
  renderNoSession = () => {
560
- const props = {
561
- style: {
562
- height: this.props.height + 'px'
563
- }
564
- }
565
551
  return (
566
- <div className='no-sessions electerm-logo-bg' {...props}>
567
- <Button
568
- onClick={this.handleNewTab}
569
- size='large'
570
- className='mg1r mg1b add-new-tab-btn'
571
- >
572
- {e('newTab')}
573
- </Button>
574
- <Button
575
- onClick={this.handleNewSsh}
576
- size='large'
577
- className='mg1r mg1b'
578
- >
579
- {e('newBookmark')}
580
- </Button>
581
- <div className='pd3'>
582
- <LogoElem />
583
- </div>
584
- </div>
552
+ <NoSession
553
+ height={this.props.height}
554
+ onNewTab={this.handleNewTab}
555
+ onNewSsh={this.handleNewSsh}
556
+ batch={this.props.batch}
557
+ />
585
558
  )
586
559
  }
587
560
 
@@ -0,0 +1,40 @@
1
+ import { Button } from 'antd'
2
+ import LogoElem from '../common/logo-elem.jsx'
3
+ import HistoryPanel from '../sidebar/history'
4
+
5
+ const e = window.translate
6
+
7
+ export default function NoSessionPanel ({ height, onNewTab, onNewSsh, batch }) {
8
+ const props = {
9
+ style: {
10
+ height: height + 'px'
11
+ }
12
+ }
13
+ const handleClick = () => {
14
+ window.openTabBatch = batch
15
+ }
16
+ return (
17
+ <div className='no-sessions electerm-logo-bg' {...props}>
18
+ <Button
19
+ onClick={onNewTab}
20
+ size='large'
21
+ className='mg1r mg1b add-new-tab-btn'
22
+ >
23
+ {e('newTab')}
24
+ </Button>
25
+ <Button
26
+ onClick={onNewSsh}
27
+ size='large'
28
+ className='mg1r mg1b'
29
+ >
30
+ {e('newBookmark')}
31
+ </Button>
32
+ <div className='pd3'>
33
+ <LogoElem />
34
+ </div>
35
+ <div className='no-session-history' onClick={handleClick}>
36
+ <HistoryPanel sort />
37
+ </div>
38
+ </div>
39
+ )
40
+ }
@@ -229,3 +229,9 @@
229
229
  padding 0 4px
230
230
  height 20px
231
231
  line-height 21px
232
+ .no-session-history
233
+ position absolute
234
+ top 280px
235
+ bottom 80px
236
+ left 0
237
+ right 0
@@ -1306,10 +1306,10 @@ clear\r`
1306
1306
  }
1307
1307
 
1308
1308
  handleShowInfo = () => {
1309
- const { id, sessionId, logName, tab } = this.props
1309
+ const { sessionId, logName, tab } = this.props
1310
1310
  const infoProps = {
1311
1311
  logName,
1312
- id,
1312
+ id: tab.id,
1313
1313
  pid: tab.id,
1314
1314
  sessionId,
1315
1315
  isRemote: this.isRemote(),
@@ -16,12 +16,14 @@ import {
16
16
  terminalActions
17
17
  } from '../common/constants'
18
18
  import * as ls from '../common/safe-local-storage'
19
+ import { action } from 'manate'
19
20
 
20
21
  const e = window.translate
22
+ const { assign } = Object
21
23
 
22
24
  export default Store => {
23
25
  Store.prototype.storeAssign = function (updates) {
24
- Object.assign(this, updates)
26
+ assign(window.store, updates)
25
27
  }
26
28
 
27
29
  Store.prototype.onError = function (e) {
@@ -47,13 +49,23 @@ export default Store => {
47
49
  })
48
50
  }
49
51
 
50
- Store.prototype.openInfoPanel = function () {
52
+ Store.prototype.openInfoPanel = action(function () {
51
53
  const { store } = window
52
54
  store.rightPanelVisible = true
55
+ store.rightPanelTab = 'info'
56
+ store.openInfoPanelAction()
57
+ })
58
+
59
+ Store.prototype.openInfoPanelAction = action(function () {
60
+ const { store } = window
53
61
  postMessage({
54
62
  action: terminalActions.showInfoPanel,
55
63
  activeTabId: store.activeTabId
56
64
  })
65
+ })
66
+
67
+ Store.prototype.toggleAIConfig = function () {
68
+ window.store.showAIConfig = !window.store.showAIConfig
57
69
  }
58
70
 
59
71
  Store.prototype.onResize = debounce(async function () {
@@ -262,4 +274,27 @@ export default Store => {
262
274
  }
263
275
  return window.store.applyProfile(tab)
264
276
  }
277
+
278
+ Store.prototype.handleOpenAIPanel = function () {
279
+ const { store } = window
280
+ store.rightPanelVisible = true
281
+ store.rightPanelTab = 'ai'
282
+ }
283
+
284
+ Store.prototype.runCommandInTerminal = function (cmd) {
285
+ postMessage({
286
+ action: terminalActions.quickCommand,
287
+ cmd,
288
+ selectedTabIds: window.store.batchInputSelectedTabIds
289
+ })
290
+ }
291
+
292
+ Store.prototype.removeAiHistory = function (id) {
293
+ const { store } = window
294
+ const index = store.aiChatHistory.findIndex(d => d.id === id)
295
+ if (index === -1) {
296
+ return
297
+ }
298
+ window.store.aiChatHistory.splice(index, 1)
299
+ }
265
300
  }
@@ -2,297 +2,9 @@
2
2
  * central state store powered by manate - https://github.com/tylerlong/manate
3
3
  */
4
4
 
5
+ import { StateStore } from './store'
5
6
  import { manage } from 'manate'
6
- import initState from './init-state'
7
- import loadDataExtend from './load-data'
8
- import dbUpgradeExtend from './db-upgrade'
9
- import eventExtend from './event'
10
- import syncExtend from './sync'
11
- import appUpgradeExtend from './app-upgrade'
12
- import bookmarkGroupExtend from './bookmark-group'
13
- import bookmarkExtend from './bookmark'
14
- import commonExtend from './common'
15
- import contextMenuExtend from './context-menu'
16
- import itemExtend from './item'
17
- import quickCommandExtend from './quick-command'
18
- import sessionExtend from './session'
19
- import settingExtend from './setting'
20
- import sidebarExtend from './sidebar'
21
- import sysMenuExtend from './system-menu'
22
- import tabExtend from './tab'
23
- import uiThemeExtend from './ui-theme'
24
- import terminalThemeExtend from './terminal-theme'
25
- import transferHistoryExtend from './transfer-history'
26
- import batchInputHistory from './batch-input-history'
27
- import transferExtend from './transfer-list'
28
- import addressBookmarkExtend from './address-bookmark'
29
- import isColorDark from '../common/is-color-dark'
30
- import { getReverseColor } from '../common/reverse-color'
31
- import { uniq } from 'lodash-es'
32
- import deepCopy from 'json-deep-copy'
33
- import {
34
- settingMap,
35
- paneMap,
36
- settingSyncId,
37
- settingShortcutsId,
38
- settingTerminalId,
39
- terminalSshConfigType
40
- } from '../common/constants'
41
- import getInitItem from '../common/init-setting-item'
42
- import {
43
- theme
44
- } from 'antd'
45
7
 
46
- const e = window.translate
47
-
48
- class Store {
49
- constructor () {
50
- Object.assign(
51
- this,
52
- initState()
53
- )
54
- }
55
-
56
- get width () {
57
- return window.store.innerWidth
58
- }
59
-
60
- get config () {
61
- return deepCopy(window.store._config)
62
- }
63
-
64
- get currentQuickCommands () {
65
- const { currentTab } = this
66
- const { quickCommands } = window.store
67
- const currentTabQuickCommands = (
68
- currentTab.quickCommands || []
69
- ).map((d, i) => {
70
- return {
71
- ...d,
72
- id: 'tab-qm-' + currentTab.id + '#' + i
73
- }
74
- })
75
- return [
76
- ...currentTabQuickCommands,
77
- ...quickCommands
78
- ]
79
- }
80
-
81
- get currentTab () {
82
- const {
83
- activeTabId
84
- } = this
85
- const { tabs } = window.store
86
- const tab = tabs.find(t => t.id === activeTabId)
87
- if (!tab) {
88
- return false
89
- }
90
- return tab
91
- }
92
-
93
- get inActiveTerminal () {
94
- const { store } = window
95
- if (store.showModal) {
96
- return false
97
- }
98
- const {
99
- currentTab
100
- } = store
101
- if (!currentTab) {
102
- return false
103
- }
104
- const {
105
- type,
106
- pane
107
- } = currentTab
108
- if (type === 'rdp' || type === 'vnc' || type === 'web') {
109
- return false
110
- }
111
- return pane === paneMap.ssh ||
112
- pane === paneMap.terminal
113
- }
114
-
115
- get quickCommandTags () {
116
- const { quickCommands } = window.store
117
- return uniq(
118
- quickCommands.reduce((p, q) => {
119
- return [
120
- ...p,
121
- ...(q.labels || [])
122
- ]
123
- }, [])
124
- )
125
- }
126
-
127
- get isTransporting () {
128
- return window.store.tabs.some(t => t.isTransporting)
129
- }
130
-
131
- get transferCount () {
132
- return window.store.fileTransfers.length
133
- }
134
-
135
- get settingSidebarList () {
136
- const {
137
- settingTab
138
- } = this
139
- const arr = window.store.getSidebarList(settingTab)
140
- const initItem = getInitItem(arr, settingTab)
141
- return settingTab === settingMap.history
142
- ? arr
143
- : [
144
- deepCopy(initItem),
145
- ...arr
146
- ]
147
- }
148
-
149
- get termSearchOptions () {
150
- const {
151
- store
152
- } = window
153
- const theme = store.getThemeConfig()
154
- return {
155
- ...window.store._termSearchOptions,
156
- decorations: {
157
- activeMatchBorder: theme.yellow,
158
- activeMatchBackground: theme.selectionBackground,
159
- activeMatchColorOverviewRuler: theme.selectionBackground,
160
- matchBackground: theme.background,
161
- matchOverviewRuler: theme.yellow,
162
- matchBorder: getReverseColor(theme.background)
163
- }
164
- }
165
- }
166
-
167
- get tabTitles () {
168
- return window.store.tabs.map(d => d.title).join('#')
169
- }
170
-
171
- get setting () {
172
- return [
173
- {
174
- id: settingTerminalId,
175
- title: e('terminal')
176
- },
177
- {
178
- id: settingShortcutsId,
179
- title: e('settingShortcuts')
180
- },
181
- {
182
- id: settingSyncId,
183
- title: e('settingSync')
184
- }
185
- ]
186
- }
187
-
188
- get onOperation () {
189
- const {
190
- store
191
- } = window
192
- return store.showModal ||
193
- store.showInfoModal ||
194
- store.showEditor ||
195
- store.showFileModal
196
- }
197
-
198
- get uiThemeConfig () {
199
- const { store } = window
200
- const themeConf = store.getUiThemeConfig()
201
- return {
202
- token: {
203
- borderRadius: 3,
204
- colorPrimary: themeConf.primary,
205
- colorBgBase: themeConf.main,
206
- colorError: themeConf.error,
207
- colorInfo: themeConf.info,
208
- colorSuccess: themeConf.success,
209
- colorWarning: themeConf.warn,
210
- colorTextBase: themeConf.text,
211
- colorLink: themeConf['text-light']
212
- },
213
- algorithm: isColorDark(themeConf.main) ? theme.darkAlgorithm : theme.defaultAlgorithm
214
- }
215
- }
216
-
217
- get bookmarkTree () {
218
- const {
219
- bookmarks
220
- } = window.store
221
- return bookmarks.reduce((p, v) => {
222
- return {
223
- ...p,
224
- [v.id]: v
225
- }
226
- }, {})
227
- }
228
-
229
- hasSshConfig () {
230
- return !!window.store
231
- .bookmarkGroups
232
- .find(b => b.id === terminalSshConfigType)
233
- }
234
-
235
- get bookmarkGroupTree () {
236
- const {
237
- bookmarkGroups
238
- } = window.store
239
- return bookmarkGroups.reduce((p, v) => {
240
- return {
241
- ...p,
242
- [v.id]: v
243
- }
244
- }, {})
245
- }
246
-
247
- get hasOldConnectionHoppingBookmark () {
248
- return window.store.bookmarks.some(b => {
249
- return b.connectionHoppings?.length && !b.hasHopping
250
- })
251
- }
252
- }
253
-
254
- const arrGetterProps = [
255
- 'addressBookmarks',
256
- 'addressBookmarksLocal',
257
- 'bookmarks',
258
- 'bookmarkGroups',
259
- 'profiles',
260
- 'quickCommands',
261
- 'terminalThemes'
262
- ]
263
-
264
- for (const prop of arrGetterProps) {
265
- Object.defineProperty(Store.prototype, prop, {
266
- get: function () {
267
- return JSON.parse(window.store[`_${prop}`] || '[]').filter(d => d)
268
- }
269
- })
270
- }
271
-
272
- loadDataExtend(Store)
273
- eventExtend(Store)
274
- dbUpgradeExtend(Store)
275
- syncExtend(Store)
276
- appUpgradeExtend(Store)
277
- bookmarkGroupExtend(Store)
278
- bookmarkExtend(Store)
279
- commonExtend(Store)
280
- contextMenuExtend(Store)
281
- itemExtend(Store)
282
- quickCommandExtend(Store)
283
- sessionExtend(Store)
284
- settingExtend(Store)
285
- sidebarExtend(Store)
286
- sysMenuExtend(Store)
287
- tabExtend(Store)
288
- terminalThemeExtend(Store)
289
- uiThemeExtend(Store)
290
- transferHistoryExtend(Store)
291
- batchInputHistory(Store)
292
- transferExtend(Store)
293
- addressBookmarkExtend(Store)
294
-
295
- export const StateStore = Store
296
- const store = manage(new Store())
8
+ const store = manage(new StateStore())
297
9
 
298
10
  export default store
@@ -19,6 +19,7 @@ import {
19
19
  dismissDelKeyTipLsKey,
20
20
  qmSortByFrequencyKey,
21
21
  resolutionsLsKey,
22
+ aiChatHistoryKey,
22
23
  splitMap
23
24
  } from '../common/constants'
24
25
  import { buildDefaultThemes } from '../common/terminal-theme'
@@ -76,6 +77,11 @@ export default () => {
76
77
  selectedSessions: [],
77
78
  sessionModalVisible: false,
78
79
 
80
+ // batch input selected tab ids
81
+ _batchInputSelectedTabIds: new Set(),
82
+ showAIConfig: false,
83
+ aiChatHistory: ls.getItemJSON(aiChatHistoryKey, []),
84
+
79
85
  // sftp
80
86
  fileOperation: fileOperationsMap.cp, // cp or mv
81
87
  pauseAllTransfer: false,
@@ -103,6 +109,7 @@ export default () => {
103
109
  activeTabId3: '',
104
110
  terminalInfoProps: {},
105
111
  rightPanelVisible: false,
112
+ rightPanelTab: 'info',
106
113
  rightPanelPinned: false,
107
114
  rightPanelWidth: parseInt(ls.getItem(rightSidebarWidthKey), 10) || 500,
108
115
 
@@ -144,7 +151,6 @@ export default () => {
144
151
  // sidebar
145
152
  openedSideBar: ls.getItem(openedSidebarKey),
146
153
  leftSidebarWidth: parseInt(ls.getItem(leftSidebarWidthKey), 10) || 300,
147
- rightSidebarWidth: parseInt(ls.getItem(rightSidebarWidthKey), 10) || 500,
148
154
  menuOpened: false,
149
155
  pinned: ls.getItem(sidebarPinnedKey) === 'true',
150
156