@electerm/electerm-react 1.101.20 → 2.1.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.
@@ -107,6 +107,12 @@ export const settingMap = buildConst([
107
107
  'profiles'
108
108
  ])
109
109
 
110
+ export const staticNewItemTabs = new Set([
111
+ 'terminalThemes',
112
+ 'quickCommands',
113
+ 'profiles'
114
+ ])
115
+
110
116
  export const infoTabs = buildConst([
111
117
  'info',
112
118
  'deps',
@@ -24,7 +24,7 @@ export default (arr, tab) => {
24
24
  } else if (tab === settingMap.quickCommands) {
25
25
  return {
26
26
  id: '',
27
- name: encodeURIComponent(newQuickCommand)
27
+ name: e(newQuickCommand)
28
28
  }
29
29
  } else if (tab === settingMap.profiles) {
30
30
  return {
@@ -138,13 +138,6 @@ export default function QuickCommandForm (props) {
138
138
  const wiki = 'https://github.com/electerm/electerm/wiki/quick-command-templates'
139
139
  return (
140
140
  <>
141
- <p>
142
- <b className='mg1r'>{e('templates')}:</b>
143
- <span className='mg1r'>{templatesStr}</span>
144
- <HelpIcon
145
- link={wiki}
146
- />
147
- </p>
148
141
  <Form
149
142
  form={form}
150
143
  onFinish={handleSubmit}
@@ -210,6 +203,13 @@ export default function QuickCommandForm (props) {
210
203
  </Button>
211
204
  </p>
212
205
  </FormItem>
206
+ <p>
207
+ <b className='mg1r'>{e('templates')}:</b>
208
+ <span className='mg1r'>{templatesStr}</span>
209
+ <HelpIcon
210
+ link={wiki}
211
+ />
212
+ </p>
213
213
  </Form>
214
214
  </>
215
215
  )
@@ -9,7 +9,8 @@ import createName, { createTitleTag } from '../../common/create-title'
9
9
  import classnames from 'classnames'
10
10
  import { noop } from 'lodash-es'
11
11
  import highlight from '../common/highlight'
12
- import { settingSyncId, settingCommonId } from '../../common/constants'
12
+ import { settingSyncId, settingCommonId, staticNewItemTabs } from '../../common/constants'
13
+ import getInitItem from '../../common/init-setting-item'
13
14
  import './list.styl'
14
15
 
15
16
  const e = window.translate
@@ -95,6 +96,17 @@ export default class ItemList extends React.PureComponent {
95
96
  return icon
96
97
  }
97
98
 
99
+ renderNewItem () {
100
+ const { type } = this.props
101
+
102
+ if (!staticNewItemTabs.has(type)) {
103
+ return null
104
+ }
105
+
106
+ const newItem = getInitItem([], type)
107
+ return this.renderItem(newItem, -1)
108
+ }
109
+
98
110
  renderItem = (item, i) => {
99
111
  const { onClickItem, type, activeItemId } = this.props
100
112
  const { id } = item
@@ -175,6 +187,7 @@ export default class ItemList extends React.PureComponent {
175
187
  {this.renderLabels ? this.renderLabels() : null}
176
188
  {this.renderSearch()}
177
189
  <div className='item-list-wrap' style={listStyle}>
190
+ {this.renderNewItem()}
178
191
  {
179
192
  list.map(this.renderItem)
180
193
  }
@@ -38,7 +38,7 @@ export default class ScrollFiles extends Component {
38
38
  pageSize: this.state.pageSize,
39
39
  total: this.props.list.length,
40
40
  showSizeChanger: false,
41
- size: 'small',
41
+ simple: true,
42
42
  onChange: this.onChange
43
43
  }
44
44
  return (
@@ -144,6 +144,14 @@ class ShortcutControl extends React.PureComponent {
144
144
  window.store.cloneToNextLayout()
145
145
  }, 500)
146
146
 
147
+ duplicateTabShortcut = throttle((e) => {
148
+ e.stopPropagation()
149
+ const { activeTabId } = window.store
150
+ if (activeTabId) {
151
+ window.store.duplicateTab(activeTabId)
152
+ }
153
+ }, 500)
154
+
147
155
  prevTabShortcut = throttle((e) => {
148
156
  e.stopPropagation()
149
157
  window.store.clickPrevTab()
@@ -21,6 +21,11 @@ export default () => {
21
21
  shortcut: 'ctrl+/',
22
22
  shortcutMac: 'meta+/'
23
23
  },
24
+ {
25
+ name: 'app_duplicateTab',
26
+ shortcut: 'alt+c',
27
+ shortcutMac: 'alt+c'
28
+ },
24
29
  {
25
30
  name: 'app_newBookmark',
26
31
  shortcut: 'ctrl+n',
@@ -64,27 +64,31 @@ class MenuBtn extends PureComponent {
64
64
  }
65
65
 
66
66
  renderContext = () => {
67
- return [
67
+ const items = [
68
68
  {
69
69
  func: 'onNewSsh',
70
70
  icon: 'CodeFilled',
71
71
  text: e('newBookmark'),
72
72
  subText: this.getShortcut('app_newBookmark')
73
- },
74
- {
73
+ }
74
+ ]
75
+ if (window.store.hasNodePty) {
76
+ items.push({
75
77
  func: 'addTab',
76
78
  icon: 'RightSquareFilled',
77
79
  text: e('newTab')
78
- },
79
- // {
80
- // type: 'hr'
81
- // },
82
- {
83
- noCloseMenu: true,
84
- icon: 'BookOutlined',
85
- text: e('bookmarks'),
86
- submenu: 'Bookmark'
87
- },
80
+ })
81
+ }
82
+ // {
83
+ // type: 'hr'
84
+ // },
85
+ items.push({
86
+ noCloseMenu: true,
87
+ icon: 'BookOutlined',
88
+ text: e('bookmarks'),
89
+ submenu: 'Bookmark'
90
+ })
91
+ items.push(
88
92
  {
89
93
  noCloseMenu: true,
90
94
  icon: 'ClockCircleOutlined',
@@ -162,7 +166,8 @@ class MenuBtn extends PureComponent {
162
166
  icon: 'CloseOutlined',
163
167
  text: e('close')
164
168
  }
165
- ]
169
+ )
170
+ return items
166
171
  }
167
172
 
168
173
  renderMenu () {
@@ -124,6 +124,10 @@ export default class Tabs extends Component {
124
124
  }
125
125
 
126
126
  handleTabAdd = () => {
127
+ if (!window.store.hasNodePty) {
128
+ window.store.onNewSsh()
129
+ return
130
+ }
127
131
  window.store.addTab(
128
132
  undefined, undefined,
129
133
  this.props.batch
@@ -195,6 +199,16 @@ export default class Tabs extends Component {
195
199
  renderMenus () {
196
200
  const { onNewSsh } = window.store
197
201
  const cls = 'pd2x pd1y context-item pointer'
202
+ const addTabBtn = window.store.hasNodePty
203
+ ? (
204
+ <div
205
+ className={cls}
206
+ onClick={this.handleTabAdd}
207
+ >
208
+ <RightSquareFilled /> {e('newTab')}
209
+ </div>
210
+ )
211
+ : null
198
212
  return (
199
213
  <div
200
214
  className='add-menu-wrap' style={{
@@ -207,12 +221,7 @@ export default class Tabs extends Component {
207
221
  >
208
222
  <CodeFilled /> {e('newBookmark')}
209
223
  </div>
210
- <div
211
- className={cls}
212
- onClick={this.handleTabAdd}
213
- >
214
- <RightSquareFilled /> {e('newTab')}
215
- </div>
224
+ {addTabBtn}
216
225
  <BookmarksList
217
226
  store={window.store}
218
227
  />
@@ -13,8 +13,8 @@ export default function NoSessionPanel ({ height, onNewTab, onNewSsh, batch }) {
13
13
  const handleClick = () => {
14
14
  window.openTabBatch = batch
15
15
  }
16
- return (
17
- <div className='no-sessions electerm-logo-bg' {...props}>
16
+ const newTabDom = window.store.hasNodePty
17
+ ? (
18
18
  <Button
19
19
  onClick={onNewTab}
20
20
  size='large'
@@ -22,6 +22,11 @@ export default function NoSessionPanel ({ height, onNewTab, onNewSsh, batch }) {
22
22
  >
23
23
  {e('newTab')}
24
24
  </Button>
25
+ )
26
+ : null
27
+ return (
28
+ <div className='no-sessions electerm-logo-bg' {...props}>
29
+ {newTabDom}
25
30
  <Button
26
31
  onClick={onNewSsh}
27
32
  size='large'
@@ -220,7 +220,11 @@ class Tab extends Component {
220
220
  }
221
221
 
222
222
  newTab = () => {
223
- this.props.addTab()
223
+ if (window.store.hasNodePty) {
224
+ this.props.addTab()
225
+ } else {
226
+ window.store.onNewSsh()
227
+ }
224
228
  }
225
229
 
226
230
  doRename = () => {
@@ -264,6 +268,7 @@ class Tab extends Component {
264
268
  const reloadShortcut = this.getShortcut('app_reloadCurrentTab')
265
269
  const closeShortcut = this.getShortcut('app_closeCurrentTab')
266
270
  const cloneToNextShortcut = this.getShortcut('app_cloneToNextLayout')
271
+ const duplicateShortcut = this.getShortcut('app_duplicateTab')
267
272
 
268
273
  const x = [
269
274
  {
@@ -290,7 +295,8 @@ class Tab extends Component {
290
295
  {
291
296
  key: 'handleDup',
292
297
  icon: <iconsMap.CopyOutlined />,
293
- label: e('duplicate')
298
+ label: e('duplicate'),
299
+ extra: duplicateShortcut
294
300
  },
295
301
  {
296
302
  key: 'cloneToNextLayout',
@@ -7,7 +7,8 @@ import { LoadingOutlined, CheckCircleOutlined } from '@ant-design/icons'
7
7
  import { pick } from 'lodash-es'
8
8
  import { Pagination } from 'antd'
9
9
  import ThemeListItem from './theme-list-item'
10
- import { defaultTheme } from '../../common/constants'
10
+ import { defaultTheme, settingMap } from '../../common/constants'
11
+ import getInitItem from '../../common/init-setting-item'
11
12
  import './terminal-theme-list.styl'
12
13
 
13
14
  const e = window.translate
@@ -58,13 +59,33 @@ export default class ThemeList extends List {
58
59
  )
59
60
  }
60
61
 
62
+ renderNewItem () {
63
+ const newThemeItem = getInitItem([], settingMap.terminalThemes)
64
+ const itemProps = {
65
+ item: newThemeItem,
66
+ renderDelBtn: this.renderDelBtn,
67
+ activeItemId: this.props.activeItemId,
68
+ ...pick(
69
+ this.props,
70
+ [
71
+ 'onClickItem',
72
+ 'theme',
73
+ 'keyword'
74
+ ]
75
+ )
76
+ }
77
+ return (
78
+ <ThemeListItem key='new-theme' {...itemProps} />
79
+ )
80
+ }
81
+
61
82
  filter = list => {
62
- const { keyword, ready } = this.state
83
+ const { keyword } = this.state
63
84
  return keyword
64
- ? list.slice(0, ready ? list.length : 2).filter(item => {
85
+ ? list.filter(item => {
65
86
  return item.name.toLowerCase().includes(keyword.toLowerCase())
66
87
  })
67
- : list.slice(0, ready ? list.length : 2)
88
+ : list
68
89
  }
69
90
 
70
91
  paged = list => {
@@ -77,6 +98,13 @@ export default class ThemeList extends List {
77
98
 
78
99
  render () {
79
100
  const { ready, page, pageSize } = this.state
101
+ if (!ready) {
102
+ return (
103
+ <div className='pd3 aligncenter'>
104
+ <LoadingOutlined />
105
+ </div>
106
+ )
107
+ }
80
108
  let {
81
109
  list = [],
82
110
  type,
@@ -92,6 +120,7 @@ export default class ThemeList extends List {
92
120
  {this.renderSearch()}
93
121
  {this.renderCurrentTheme()}
94
122
  <div className='item-list-wrap' style={listStyle}>
123
+ {this.renderNewItem()}
95
124
  {
96
125
  list.map(this.renderItem)
97
126
  }
@@ -101,17 +130,10 @@ export default class ThemeList extends List {
101
130
  total={all}
102
131
  current={page}
103
132
  pageSize={pageSize}
133
+ showLessItems
134
+ simple
104
135
  onShowSizeChange={this.handlePageSizeChange}
105
136
  />
106
- {
107
- ready
108
- ? null
109
- : (
110
- <div className='pd3 aligncenter'>
111
- <LoadingOutlined />
112
- </div>
113
- )
114
- }
115
137
  </div>
116
138
  )
117
139
  }
@@ -309,6 +309,10 @@ export default class ItemListTree extends Component {
309
309
  store.storeAssign({
310
310
  currentBookmarkGroupId: id
311
311
  })
312
+ const func = this.props.expandedKeys.includes(id)
313
+ ? this.onUnExpandKey
314
+ : this.onExpandKey
315
+ func({ id })
312
316
  } else {
313
317
  store.storeAssign({
314
318
  currentBookmarkGroupId: findBookmarkGroupId(store.bookmarkGroups, id)
@@ -12,31 +12,55 @@ export default (Store) => {
12
12
  return false
13
13
  }
14
14
  const shouldUpgrade = await window.pre.runGlobalAsync('checkDbUpgrade')
15
- if (!shouldUpgrade) {
15
+ const shouldMigrate = await window.pre.runGlobalAsync('checkMigrate')
16
+ if (!shouldUpgrade && !shouldMigrate) {
17
+ window.migrating = false
16
18
  return false
17
19
  }
18
- const {
19
- dbVersion,
20
- packVersion
21
- } = shouldUpgrade
22
- const mod = Modal.info({
23
- title: 'Upgrading database',
24
- content: `Upgrading database... from v${dbVersion} to v${packVersion} please wait`,
20
+ window.migrating = true
21
+ let mod
22
+ const commonProps = {
25
23
  keyboard: false,
26
24
  okButtonProps: {
27
25
  style: {
28
26
  display: 'none'
29
27
  }
30
28
  }
31
- })
32
- await window.pre.runGlobalAsync('doUpgrade')
33
- mod.update({
34
- title: 'Done',
35
- content: 'Database Upgraded',
36
- okButtonProps: {}
37
- })
38
- await delay(2000)
39
- mod.destroy()
29
+ }
30
+ if (shouldMigrate) {
31
+ mod = Modal.info({
32
+ title: 'Migrating database',
33
+ content: 'Migrating database... please wait',
34
+ ...commonProps
35
+ })
36
+ await window.pre.runGlobalAsync('migrate')
37
+ mod.update({
38
+ title: 'Done',
39
+ content: 'Database Migrated',
40
+ okButtonProps: {}
41
+ })
42
+ await delay(2000)
43
+ mod.destroy()
44
+ }
45
+ if (shouldUpgrade) {
46
+ const {
47
+ dbVersion,
48
+ packVersion
49
+ } = shouldUpgrade
50
+ mod = Modal.info({
51
+ title: 'Upgrading database',
52
+ content: `Upgrading database... from v${dbVersion} to v${packVersion} please wait`,
53
+ ...commonProps
54
+ })
55
+ await window.pre.runGlobalAsync('doUpgrade')
56
+ mod.update({
57
+ title: 'Done',
58
+ content: 'Database Upgraded',
59
+ okButtonProps: {}
60
+ })
61
+ await delay(2000)
62
+ mod.destroy()
63
+ }
40
64
  await store.restart()
41
65
  return true
42
66
  }
@@ -188,6 +188,7 @@ export default () => {
188
188
  innerWidth: window.innerWidth,
189
189
  height: 500,
190
190
  isMaximized: window.pre.runSync('isMaximized'),
191
+ hasNodePty: window.pre.runSync('nodePtyCheck'),
191
192
  terminalFullScreen: false,
192
193
  hideDelKeyTip: ls.getItem(dismissDelKeyTipLsKey) === 'y',
193
194
  tabsHeight: 36
@@ -32,7 +32,8 @@ import getBrand from '../components/ai/get-brand'
32
32
  import {
33
33
  settingMap,
34
34
  terminalSshConfigType,
35
- paneMap
35
+ paneMap,
36
+ staticNewItemTabs
36
37
  } from '../common/constants'
37
38
  import getInitItem from '../common/init-setting-item'
38
39
  import createTitle from '../common/create-title'
@@ -149,10 +150,12 @@ class Store {
149
150
  const initItem = getInitItem(arr, settingTab)
150
151
  return settingTab === settingMap.history
151
152
  ? arr
152
- : [
153
- deepCopy(initItem),
154
- ...arr
155
- ]
153
+ : staticNewItemTabs.has(settingTab)
154
+ ? arr // Don't add initItem for these tabs, they will be handled separately
155
+ : [
156
+ deepCopy(initItem),
157
+ ...arr
158
+ ]
156
159
  }
157
160
 
158
161
  get terminalCommandSuggestions () {
@@ -10,6 +10,7 @@ import {
10
10
  maxHistory
11
11
  } from '../common/constants'
12
12
  import { refs } from '../components/common/ref'
13
+ import { message } from 'antd'
13
14
  import * as ls from '../common/safe-local-storage'
14
15
  import deepCopy from 'json-deep-copy'
15
16
  import generate from '../common/id-with-stamp'
@@ -312,6 +313,15 @@ export default Store => {
312
313
  index,
313
314
  batch
314
315
  ) {
316
+ if (
317
+ (!newTab.type || newTab.type === 'local') &&
318
+ !newTab.host &&
319
+ !window.store.hasNodePty
320
+ ) {
321
+ return message.warning(
322
+ 'local terminal is not supported, due to node-pty not working in this build'
323
+ )
324
+ }
315
325
  const { store } = window
316
326
  const { tabs } = store
317
327
  newTab.tabCount = store.nextTabCount()
@@ -40,6 +40,9 @@ export default store => {
40
40
 
41
41
  for (const name of dbNamesForWatch) {
42
42
  window[`watch${name}`] = autoRun(async () => {
43
+ if (window.migrating) {
44
+ return
45
+ }
43
46
  const old = refsStatic.get('oldState-' + name)
44
47
  const n = store.getItems(name)
45
48
  const { updated, added, removed } = dataCompare(
@@ -55,9 +58,10 @@ export default store => {
55
58
  for (const item of added) {
56
59
  await insert(name, item)
57
60
  }
61
+ const newOrder = (n || []).map(d => d.id)
58
62
  await update(
59
63
  `${name}:order`,
60
- (n || []).map(d => d.id)
64
+ newOrder
61
65
  )
62
66
  refsStatic.add('oldState-' + name, deepCopy(n) || [])
63
67
  if (name === 'bookmarks') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "1.101.20",
3
+ "version": "2.1.16",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",