@electerm/electerm-react 3.8.15 → 3.9.15

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 (35) hide show
  1. package/client/common/pre.js +0 -1
  2. package/client/components/bookmark-form/bookmark-from-history-modal.jsx +0 -1
  3. package/client/components/bookmark-form/config/rdp.js +4 -2
  4. package/client/components/icons/heartbeat.jsx +23 -0
  5. package/client/components/main/main.jsx +3 -1
  6. package/client/components/session/session.jsx +3 -3
  7. package/client/components/setting-sync/setting-sync-form.jsx +9 -1
  8. package/client/components/setting-sync/setting-sync.jsx +2 -1
  9. package/client/components/sftp/list-table-ui.jsx +33 -54
  10. package/client/components/sftp/paged-list.jsx +44 -44
  11. package/client/components/sftp/sftp-entry.jsx +5 -4
  12. package/client/components/sidebar/info-modal.jsx +8 -2
  13. package/client/components/tabs/tab.jsx +38 -19
  14. package/client/components/tabs/tabs.styl +8 -17
  15. package/client/components/terminal/attach-addon-custom.js +7 -3
  16. package/client/components/terminal/reconnect-overlay.jsx +2 -15
  17. package/client/components/terminal/terminal-command-dropdown.jsx +1 -1
  18. package/client/components/terminal/terminal-error-handle.jsx +43 -0
  19. package/client/components/terminal/terminal.jsx +40 -38
  20. package/client/components/terminal/terminal.styl +12 -7
  21. package/client/components/terminal/unix-timestamp-tooltip.jsx +85 -0
  22. package/client/components/text-editor/edit-with-custom-editor.jsx +22 -3
  23. package/client/components/text-editor/text-editor.jsx +21 -0
  24. package/client/components/tree-list/bookmark-toolbar.jsx +5 -5
  25. package/client/components/tree-list/tree-list-item.jsx +6 -12
  26. package/client/components/tree-list/tree-list-row.jsx +5 -0
  27. package/client/components/tree-list/tree-list-rows.js +3 -1
  28. package/client/components/widgets/widget-control.jsx +1 -0
  29. package/client/store/common.js +1 -1
  30. package/client/store/load-data.js +2 -2
  31. package/client/store/mcp-handler.js +83 -10
  32. package/client/store/sync.js +3 -2
  33. package/client/store/tab.js +34 -0
  34. package/package.json +1 -1
  35. package/client/components/terminal/socket-close-warning.jsx +0 -94
@@ -1,27 +1,14 @@
1
1
  import { memo } from 'react'
2
- import { Button } from 'antd'
3
- import { LoadingOutlined } from '@ant-design/icons'
4
2
 
5
3
  const e = window.translate
6
4
 
7
- export default memo(function ReconnectOverlay ({ countdown, onCancel }) {
5
+ export default memo(function ReconnectOverlay ({ countdown }) {
8
6
  if (countdown === null || countdown === undefined) {
9
7
  return null
10
8
  }
11
9
  return (
12
10
  <div className='terminal-reconnect-overlay'>
13
- <div className='terminal-reconnect-box'>
14
- <LoadingOutlined className='terminal-reconnect-icon' />
15
- <div className='terminal-reconnect-msg'>
16
- {e('autoReconnectTerminal')}: {countdown}s
17
- </div>
18
- <Button
19
- size='small'
20
- onClick={onCancel}
21
- >
22
- {e('cancel')}
23
- </Button>
24
- </div>
11
+ {e('autoReconnectTerminal')}: {countdown}s
25
12
  </div>
26
13
  )
27
14
  })
@@ -278,7 +278,7 @@ export default class TerminalCmdSuggestions extends Component {
278
278
  id: uid(),
279
279
  command: b.password,
280
280
  type: 'PW',
281
- hint: [b.username, b.host].filter(Boolean).join('@')
281
+ hint: [b.username, [b.host, b.port].filter(Boolean).join(':')].filter(Boolean).join('@')
282
282
  })
283
283
  }
284
284
  }
@@ -0,0 +1,43 @@
1
+ import { memo } from 'react'
2
+ import {
3
+ Button,
4
+ Alert
5
+ } from 'antd'
6
+
7
+ const e = window.translate
8
+
9
+ export default memo(function TerminalErrorHandle ({
10
+ errorMessage,
11
+ showEditBookmarkButton,
12
+ onEditBookmark
13
+ }) {
14
+ if (!errorMessage) {
15
+ return null
16
+ }
17
+
18
+ function renderEditBookmarkButton () {
19
+ if (!showEditBookmarkButton) {
20
+ return null
21
+ }
22
+ return (
23
+ <div className='terminal-error-actions pd1y'>
24
+ <Button
25
+ onClick={onEditBookmark}
26
+ >
27
+ {e('edit')} {e('bookmarks')}
28
+ </Button>
29
+ </div>
30
+ )
31
+ }
32
+
33
+ return (
34
+ <Alert
35
+ className='terminal-error-handle'
36
+ message={errorMessage}
37
+ type='error'
38
+ showIcon
39
+ banner
40
+ description={renderEditBookmarkButton()}
41
+ />
42
+ )
43
+ })
@@ -1,5 +1,4 @@
1
1
  import { Component, createRef } from 'react'
2
- import { handleErr } from '../../common/fetch.jsx'
3
2
  import { isEqual, pick, debounce, throttle } from 'lodash-es'
4
3
  import clone from '../../common/to-simple-obj.js'
5
4
  import resolve from '../../common/resolve.js'
@@ -7,7 +6,6 @@ import {
7
6
  Spin,
8
7
  Dropdown
9
8
  } from 'antd'
10
- import { notification } from '../common/notification'
11
9
  import message from '../common/message'
12
10
  import Modal from '../common/modal'
13
11
  import classnames from 'classnames'
@@ -48,8 +46,8 @@ import ExternalLink from '../common/external-link.jsx'
48
46
  import createDefaultLogPath from '../../common/default-log-path.js'
49
47
  import SearchResultBar from './terminal-search-bar'
50
48
  import RemoteFloatControl from '../common/remote-float-control'
51
- import { showSocketCloseWarning } from './socket-close-warning.jsx'
52
49
  import ReconnectOverlay from './reconnect-overlay.jsx'
50
+ import TerminalErrorHandle from './terminal-error-handle.jsx'
53
51
  import {
54
52
  loadTerminal,
55
53
  loadFitAddon,
@@ -78,6 +76,7 @@ class Term extends Component {
78
76
  matchIndex: -1,
79
77
  totalLines: 0,
80
78
  reconnectCountdown: null,
79
+ terminalError: null,
81
80
  dropFileModalVisible: false,
82
81
  droppedFiles: []
83
82
  }
@@ -169,11 +168,6 @@ class Term extends Component {
169
168
  this.fitAddon = null
170
169
  this.cmdAddon = null
171
170
  this.imageAddon = null
172
- // Clear the notification if it exists
173
- if (this.socketCloseWarning) {
174
- notification.destroy(this.socketCloseWarning.key)
175
- this.socketCloseWarning = null
176
- }
177
171
  }
178
172
 
179
173
  terminalConfigProps = [
@@ -836,6 +830,7 @@ class Term extends Component {
836
830
  }
837
831
 
838
832
  onPasswordPromptDetected = () => {
833
+ window.store.notifyTabPasswordPrompt(this.props.tab.id)
839
834
  if (!this.props.config.showCmdSuggestions) {
840
835
  return
841
836
  }
@@ -848,6 +843,7 @@ class Term extends Component {
848
843
  }
849
844
 
850
845
  onPasswordPromptCancelled = () => {
846
+ window.store.clearTabPasswordPrompt(this.props.tab.id)
851
847
  const suggestions = refsStatic.get('terminal-suggestions')
852
848
  if (suggestions?.state?.passwordMode) {
853
849
  suggestions.closeSuggestions()
@@ -958,9 +954,10 @@ class Term extends Component {
958
954
  }
959
955
 
960
956
  onSelectionChange = () => {
961
- this.setState({
962
- hasSelection: this.term.hasSelection()
963
- })
957
+ const hasSelection = this.term.hasSelection()
958
+ const txt = hasSelection ? this.term.getSelection().trim() : ''
959
+ this.setState({ hasSelection })
960
+ refsStatic.get('unix-timestamp-tooltip')?.onSelection(txt)
964
961
  }
965
962
 
966
963
  // setActive = () => {
@@ -1170,7 +1167,8 @@ class Term extends Component {
1170
1167
 
1171
1168
  remoteInit = async (term = this.term) => {
1172
1169
  this.setState({
1173
- loading: true
1170
+ loading: true,
1171
+ terminalError: null
1174
1172
  })
1175
1173
  const { cols, rows } = term
1176
1174
  const { config } = this.props
@@ -1248,7 +1246,7 @@ class Term extends Component {
1248
1246
  .catch(err => {
1249
1247
  if (!isAutoReconnect) {
1250
1248
  const text = err.message
1251
- handleErr({ message: text })
1249
+ this.handleError({ message: text, from, srcId })
1252
1250
  }
1253
1251
  })
1254
1252
  if (typeof r === 'string' && r.includes('fail')) {
@@ -1304,6 +1302,29 @@ class Term extends Component {
1304
1302
  )
1305
1303
  }
1306
1304
 
1305
+ handleError = ({ message: errorMessage, from, srcId }) => {
1306
+ this.setState({
1307
+ terminalError: {
1308
+ message: errorMessage || 'Failed to create terminal session',
1309
+ from,
1310
+ srcId
1311
+ }
1312
+ })
1313
+ }
1314
+
1315
+ handleEditBookmarkFromError = () => {
1316
+ const error = this.state.terminalError
1317
+ if (!error || error.from !== 'bookmarks' || !error.srcId) {
1318
+ return
1319
+ }
1320
+ const item = window.store.bookmarksMap?.get(error.srcId) ||
1321
+ window.store.bookmarks?.find(d => d.id === error.srcId)
1322
+ if (!item) {
1323
+ return
1324
+ }
1325
+ window.store.openBookmarkEdit(item)
1326
+ }
1327
+
1307
1328
  initSocketEvents = () => {
1308
1329
  const originalSend = this.socket.send
1309
1330
  this.socket.send = (data) => {
@@ -1369,31 +1390,8 @@ class Term extends Component {
1369
1390
  return this.props.delTab(this.props.tab.id)
1370
1391
  }
1371
1392
  const { autoReconnectTerminal } = this.props.config
1372
- const isActive = this.isActiveTerminal()
1373
- const isFocused = window.focused
1374
1393
  if (autoReconnectTerminal) {
1375
- if (isActive && isFocused) {
1376
- this.socketCloseWarning = showSocketCloseWarning({
1377
- tabId: this.props.tab.id,
1378
- tab: this.props.tab,
1379
- autoReconnect: true,
1380
- delTab: this.props.delTab,
1381
- reloadTab: this.props.reloadTab
1382
- })
1383
- } else {
1384
- this.scheduleAutoReconnect(3000)
1385
- }
1386
- } else {
1387
- if (!isActive || !isFocused) {
1388
- return false
1389
- }
1390
- this.socketCloseWarning = showSocketCloseWarning({
1391
- tabId: this.props.tab.id,
1392
- tab: this.props.tab,
1393
- autoReconnect: false,
1394
- delTab: this.props.delTab,
1395
- reloadTab: this.props.reloadTab
1396
- })
1394
+ this.scheduleAutoReconnect(3000)
1397
1395
  }
1398
1396
  }
1399
1397
 
@@ -1547,9 +1545,13 @@ class Term extends Component {
1547
1545
  <RemoteFloatControl
1548
1546
  isFullScreen={fullscreen}
1549
1547
  />
1548
+ <TerminalErrorHandle
1549
+ errorMessage={this.state.terminalError?.message}
1550
+ showEditBookmarkButton={this.state.terminalError?.from === 'bookmarks' && !!this.state.terminalError?.srcId}
1551
+ onEditBookmark={this.handleEditBookmarkFromError}
1552
+ />
1550
1553
  <ReconnectOverlay
1551
1554
  countdown={this.state.reconnectCountdown}
1552
- onCancel={this.handleCancelAutoReconnect}
1553
1555
  />
1554
1556
  <DropFileModal
1555
1557
  visible={this.state.dropFileModalVisible}
@@ -153,12 +153,17 @@
153
153
 
154
154
  .terminal-reconnect-overlay
155
155
  position absolute
156
- left 0
157
- top 0
158
- width 100%
159
- height 100%
160
- display flex
161
- align-items center
162
- justify-content center
156
+ bottom 6px
157
+ right 12px
158
+ font-size 11px
159
+ color rgba(255, 255, 255, 0.55)
160
+ pointer-events none
163
161
  z-index 110
164
162
 
163
+ .terminal-error-handle
164
+ position absolute
165
+ left 0
166
+ top 0
167
+ z-index 100
168
+ background var(--main)
169
+ max-width calc(100% - 24px)
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Global tooltip that detects Unix timestamps in terminal selections
3
+ * and displays their human-readable date/time near the cursor.
4
+ * Registered via refsStatic as 'unix-timestamp-tooltip'.
5
+ */
6
+
7
+ import { Component } from 'react'
8
+ import { refsStatic } from '../common/ref'
9
+
10
+ export default class UnixTimestampTooltip extends Component {
11
+ state = {
12
+ visible: false,
13
+ x: 0,
14
+ y: 0,
15
+ text: ''
16
+ }
17
+
18
+ _mouseX = 0
19
+ _mouseY = 0
20
+
21
+ componentDidMount () {
22
+ refsStatic.add('unix-timestamp-tooltip', this)
23
+ document.addEventListener('mousemove', this.onMouseMove)
24
+ }
25
+
26
+ componentWillUnmount () {
27
+ refsStatic.remove('unix-timestamp-tooltip')
28
+ document.removeEventListener('mousemove', this.onMouseMove)
29
+ }
30
+
31
+ detectUnixTimestamp (txt) {
32
+ if (!/^\d+$/.test(txt)) return null
33
+ const num = parseInt(txt, 10)
34
+ // seconds: 9-10 digits, year ~2001-2286
35
+ if ((txt.length === 9 || txt.length === 10) && num >= 946684800 && num <= 32503680000) {
36
+ return new Date(num * 1000).toLocaleString()
37
+ }
38
+ // milliseconds: 13 digits
39
+ if (txt.length === 13 && num >= 946684800000 && num <= 32503680000000) {
40
+ return new Date(num).toLocaleString()
41
+ }
42
+ return null
43
+ }
44
+
45
+ onMouseMove = (e) => {
46
+ this._mouseX = e.clientX
47
+ this._mouseY = e.clientY
48
+ }
49
+
50
+ onSelection = (txt) => {
51
+ const ts = this.detectUnixTimestamp(txt)
52
+ if (ts) {
53
+ this.setState({ visible: true, x: this._mouseX, y: this._mouseY, text: ts })
54
+ } else {
55
+ this.setState({ visible: false })
56
+ }
57
+ }
58
+
59
+ render () {
60
+ const { visible, x, y, text } = this.state
61
+ if (!visible) {
62
+ return null
63
+ }
64
+ return (
65
+ <div
66
+ style={{
67
+ position: 'fixed',
68
+ left: x,
69
+ top: y - 36,
70
+ background: 'rgba(0,0,0,0.75)',
71
+ color: '#fff',
72
+ padding: '3px 8px',
73
+ borderRadius: 4,
74
+ fontSize: 12,
75
+ whiteSpace: 'nowrap',
76
+ pointerEvents: 'none',
77
+ transform: 'translateX(-50%)',
78
+ zIndex: 9999
79
+ }}
80
+ >
81
+ {text}
82
+ </div>
83
+ )
84
+ }
85
+ }
@@ -6,18 +6,30 @@ import { useState } from 'react'
6
6
  import { Button, Input, Space } from 'antd'
7
7
  import { safeGetItem, safeSetItem } from '../../common/safe-local-storage.js'
8
8
 
9
- const LS_KEY = 'customEditorCommand'
9
+ export const CUSTOM_EDITOR_COMMAND_LS_KEY = 'customEditorCommand'
10
+ export const CUSTOM_EDITOR_AUTO_OPEN_LS_KEY = 'customEditorAutoOpen'
10
11
  const e = window.translate
11
12
 
12
13
  export default function EditWithCustomEditor ({ loading, editWithCustom }) {
13
14
  const [editorCommand, setEditorCommand] = useState(
14
- () => safeGetItem(LS_KEY) || ''
15
+ () => safeGetItem(CUSTOM_EDITOR_COMMAND_LS_KEY) || ''
15
16
  )
17
+ const [autoOpen, setAutoOpen] = useState(
18
+ () => safeGetItem(CUSTOM_EDITOR_AUTO_OPEN_LS_KEY) === 'true'
19
+ )
20
+
21
+ const autoOpenLabel = e('autoOpen')
16
22
 
17
23
  function handleChange (ev) {
18
24
  const val = ev.target.value
19
25
  setEditorCommand(val)
20
- safeSetItem(LS_KEY, val)
26
+ safeSetItem(CUSTOM_EDITOR_COMMAND_LS_KEY, val)
27
+ }
28
+
29
+ function handleToggleAutoOpen () {
30
+ const next = !autoOpen
31
+ setAutoOpen(next)
32
+ safeSetItem(CUSTOM_EDITOR_AUTO_OPEN_LS_KEY, String(next))
21
33
  }
22
34
 
23
35
  function handleClick () {
@@ -45,6 +57,13 @@ export default function EditWithCustomEditor ({ loading, editWithCustom }) {
45
57
  onChange={handleChange}
46
58
  disabled={loading}
47
59
  />
60
+ <Button
61
+ type={autoOpen ? 'primary' : 'default'}
62
+ disabled={loading}
63
+ onClick={handleToggleAutoOpen}
64
+ >
65
+ {autoOpenLabel}: {autoOpen ? 'On' : 'Off'}
66
+ </Button>
48
67
  </Space.Compact>
49
68
  )
50
69
  }
@@ -4,9 +4,14 @@
4
4
 
5
5
  import { PureComponent } from 'react'
6
6
  import TextEditorForm from './text-editor-form'
7
+ import {
8
+ CUSTOM_EDITOR_AUTO_OPEN_LS_KEY,
9
+ CUSTOM_EDITOR_COMMAND_LS_KEY
10
+ } from './edit-with-custom-editor'
7
11
  import { Spin } from 'antd'
8
12
  import Modal from '../common/modal'
9
13
  import resolve from '../../common/resolve'
14
+ import { safeGetItem } from '../../common/safe-local-storage.js'
10
15
  import { refsStatic, refs } from '../common/ref'
11
16
 
12
17
  const e = window.translate
@@ -69,12 +74,28 @@ export default class TextEditor extends PureComponent {
69
74
  return
70
75
  }
71
76
  const text = await fileRef.fetchEditorText(p, type)
77
+ const editorCommand = this.getAutoOpenCustomEditorCommand()
72
78
  this.setStateProxy({
73
79
  text,
74
80
  loading: false
81
+ }, () => {
82
+ if (editorCommand) {
83
+ this.editWithCustom(editorCommand)
84
+ }
75
85
  })
76
86
  }
77
87
 
88
+ getAutoOpenCustomEditorCommand = () => {
89
+ if (window.et.isWebApp) {
90
+ return ''
91
+ }
92
+ const autoOpen = safeGetItem(CUSTOM_EDITOR_AUTO_OPEN_LS_KEY) === 'true'
93
+ if (!autoOpen) {
94
+ return ''
95
+ }
96
+ return safeGetItem(CUSTOM_EDITOR_COMMAND_LS_KEY).trim()
97
+ }
98
+
78
99
  doSubmit = () => {
79
100
  this.handleSubmit({
80
101
  text: this.state.text
@@ -7,7 +7,7 @@ import {
7
7
  MenuOutlined,
8
8
  EditOutlined
9
9
  } from '@ant-design/icons'
10
- import { Button, Space, Dropdown } from 'antd'
10
+ import { Button, Space, Dropdown, Flex } from 'antd'
11
11
  import copy from 'json-deep-copy'
12
12
  import time from '../../common/time'
13
13
  import download from '../../common/download'
@@ -87,8 +87,8 @@ export default function BookmarkToolbar (props) {
87
87
  return (
88
88
 
89
89
  <div className='pd1b pd1r'>
90
- <div className='fix'>
91
- <div className='fleft'>
90
+ <Flex justify='space-between' align='center'>
91
+ <div>
92
92
  <Space.Compact>
93
93
  <Button onClick={onNewBookmark}>
94
94
  <BookOutlined className='with-plus' />
@@ -122,12 +122,12 @@ export default function BookmarkToolbar (props) {
122
122
  </Button>
123
123
  </Space.Compact>
124
124
  </div>
125
- <div className='fright'>
125
+ <div>
126
126
  <Dropdown {...ddProps}>
127
127
  <MenuOutlined />
128
128
  </Dropdown>
129
129
  </div>
130
- </div>
130
+ </Flex>
131
131
  </div>
132
132
  )
133
133
  }
@@ -3,17 +3,11 @@
3
3
  */
4
4
 
5
5
  import { memo } from 'react'
6
- import createName, { createTitleTag } from '../../common/create-title'
6
+ import { createTitleTag } from '../../common/create-title'
7
7
  import classnames from 'classnames'
8
8
  import highlight from '../common/highlight'
9
9
  import uid from '../../common/uid'
10
10
 
11
- function getItemLabel (item, isGroup) {
12
- return isGroup
13
- ? item?.title || ''
14
- : createName(item)
15
- }
16
-
17
11
  function areEqual (prevProps, nextProps) {
18
12
  const prevSelected = prevProps.selectedItemId === prevProps.item.id
19
13
  const nextSelected = nextProps.selectedItemId === nextProps.item.id
@@ -27,10 +21,10 @@ function areEqual (prevProps, nextProps) {
27
21
  prevSelected === nextSelected &&
28
22
  prevSearchSelected === nextSearchSelected &&
29
23
  prevProps.item.id === nextProps.item.id &&
30
- prevProps.item.level === nextProps.item.level &&
31
- prevProps.item.color === nextProps.item.color &&
32
- prevProps.item.description === nextProps.item.description &&
33
- getItemLabel(prevProps.item, prevProps.isGroup) === getItemLabel(nextProps.item, nextProps.isGroup)
24
+ prevProps.itemLevel === nextProps.itemLevel &&
25
+ prevProps.itemColor === nextProps.itemColor &&
26
+ prevProps.itemDescription === nextProps.itemDescription &&
27
+ prevProps.itemLabel === nextProps.itemLabel
34
28
  }
35
29
 
36
30
  function TreeListItem (props) {
@@ -87,7 +81,7 @@ function TreeListItem (props) {
87
81
  : null
88
82
  const title = isGroup
89
83
  ? item.title
90
- : createName(item)
84
+ : props.itemLabel
91
85
  const titleAll = title + (item.description ? ' - ' + item.description : '')
92
86
  const titleHighlight = isGroup
93
87
  ? item.title || 'no title'
@@ -2,6 +2,7 @@ import TreeExpander from './tree-expander'
2
2
  import TreeListItem from './tree-list-item'
3
3
  import TreeItemOp from './tree-item-op'
4
4
  import { treeLevelIndent } from './tree-list-layout'
5
+ import createName from '../../common/create-title'
5
6
 
6
7
  export default function TreeListRow (props) {
7
8
  const {
@@ -38,6 +39,10 @@ export default function TreeListRow (props) {
38
39
  item,
39
40
  isGroup,
40
41
  parentId,
42
+ itemLabel: isGroup ? (item?.title || '') : createName(item),
43
+ itemColor: item?.color,
44
+ itemDescription: item?.description,
45
+ itemLevel: item?.level,
41
46
  leftSidebarWidth,
42
47
  staticList,
43
48
  selectedItemId: activeItemId,
@@ -26,7 +26,9 @@ export function buildVisibleTreeRows ({
26
26
  const item = bookmarksMap.get(bookmarkId)
27
27
  const matched = Boolean(
28
28
  item &&
29
- (!lowerKeyword || createName(item).toLowerCase().includes(lowerKeyword))
29
+ (!lowerKeyword ||
30
+ createName(item).toLowerCase().includes(lowerKeyword) ||
31
+ (item.description || '').toLowerCase().includes(lowerKeyword))
30
32
  )
31
33
  bookmarkMatchCache.set(bookmarkId, matched)
32
34
  return matched
@@ -55,6 +55,7 @@ export default function WidgetControl ({ formData, widgetInstancesLength }) {
55
55
  showMsg(msg, 'success', result.serverInfo, 10)
56
56
  } catch (err) {
57
57
  console.error('Failed to run widget:', err)
58
+ showMsg(`Failed to run widget: ${err.message}`, 'error', null, 10)
58
59
  } finally {
59
60
  setLoading(false)
60
61
  }
@@ -298,7 +298,7 @@ export default Store => {
298
298
  }
299
299
 
300
300
  Store.prototype.aiConfigMissing = function () {
301
- return aiConfigsArr.slice(0, -1).some(k => !window.store.config[k])
301
+ return aiConfigsArr.filter(k => k !== 'apiKeyAI' && k !== 'proxyAI').some(k => !window.store.config[k])
302
302
  }
303
303
 
304
304
  Store.prototype.clearHistory = function () {
@@ -100,7 +100,7 @@ export async function addTabFromCommandLine (store, opts) {
100
100
  (conf.username && conf.host) ||
101
101
  conf.fromCmdLine
102
102
  ) {
103
- store.addTab(conf)
103
+ store.ipcOpenTab(conf)
104
104
  } else if (
105
105
  options.initFolder &&
106
106
  !(store.config.onStartSessions || []).length &&
@@ -229,7 +229,7 @@ export default (Store) => {
229
229
  Store.prototype.checkPendingDeepLink = async function () {
230
230
  const pending = await window.pre.runGlobalAsync('getPendingDeepLink')
231
231
  if (pending) {
232
- window.store.addTab(pending)
232
+ window.store.ipcOpenTab(pending)
233
233
  }
234
234
  }
235
235
  Store.prototype.parseQuickConnect = function (url) {