@electerm/electerm-react 1.60.32 → 1.60.46

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.
@@ -265,6 +265,7 @@ export const proxyHelpLink = 'https://github.com/electerm/electerm/wiki/proxy-fo
265
265
  export const regexHelpLink = 'https://github.com/electerm/electerm/wiki/Terminal-keywords-highlight-regular-expression-exmaples'
266
266
  export const connectionHoppingWikiLink = 'https://github.com/electerm/electerm/wiki/Connection-Hopping-Behavior-Change-in-electerm-since-v1.50.65'
267
267
  export const aiConfigWikiLink = 'https://github.com/electerm/electerm/wiki/AI-model-config-guide'
268
+ export const rdpWikiLink = 'https://github.com/electerm/electerm/wiki/RDP-session-known-issues'
268
269
  export const modals = {
269
270
  hide: 0,
270
271
  setting: 1,
@@ -31,7 +31,7 @@ export default {
31
31
  checkUpdateOnStart: true,
32
32
  cursorBlink: false,
33
33
  cursorStyle: 'block',
34
- useSystemTitleBar: window.pre.isLinux || window.et.isLinux || false,
34
+ useSystemTitleBar: false,
35
35
  opacity: 1,
36
36
  defaultEditor: '',
37
37
  terminalWordSeparator: './\\()"\'-:,.;<>~!@#$%^&*|+=[]{}`~ ?',
@@ -61,5 +61,6 @@ export default {
61
61
  dataSyncSelected: 'all',
62
62
  baseURLAI: 'https://api.deepseek.com',
63
63
  modelAI: 'deepseek-chat',
64
- roleAI: '终端专家,提供不同系统下安全命令,解释用法及风险,用markdown格式'
64
+ roleAI: '终端专家,提供不同系统下安全命令,解释用法及风险,用markdown格式',
65
+ apiPathAI: '/chat/completions'
65
66
  }
@@ -24,7 +24,8 @@ const aiConfigsArr = [
24
24
  'baseURLAI',
25
25
  'modelAI',
26
26
  'roleAI',
27
- 'apiKeyAI'
27
+ 'apiKeyAI',
28
+ 'apiPathAI'
28
29
  ]
29
30
 
30
31
  export default function AIChat (props) {
@@ -52,6 +53,7 @@ export default function AIChat (props) {
52
53
  props.config.modelAI,
53
54
  buildRole(),
54
55
  props.config.baseURLAI,
56
+ props.config.apiPathAI,
55
57
  props.config.apiKeyAI
56
58
  ).catch(
57
59
  window.store.onError
@@ -4,7 +4,8 @@ import {
4
4
  Button,
5
5
  AutoComplete,
6
6
  Modal,
7
- Alert
7
+ Alert,
8
+ Space
8
9
  } from 'antd'
9
10
  import { useEffect, useState } from 'react'
10
11
  import Link from '../common/external-link'
@@ -76,12 +77,14 @@ export default function AIConfigForm ({ initialValues, onSubmit, showAIConfig })
76
77
  return null
77
78
  }
78
79
  const title = 'AI ' + e('setting')
80
+ const defaultLangs = window.store.getLangNames().map(l => ({ value: l }))
79
81
  return (
80
82
  <Modal
81
83
  title={title}
82
84
  open
83
85
  onCancel={handleCancel}
84
86
  footer={null}
87
+ width='90%'
85
88
  >
86
89
  <Alert
87
90
  message={
@@ -96,23 +99,38 @@ export default function AIConfigForm ({ initialValues, onSubmit, showAIConfig })
96
99
  initialValues={initialValues}
97
100
  layout='vertical'
98
101
  >
99
- <Form.Item
100
- label='API URL'
101
- name='baseURLAI'
102
- rules={[
103
- { required: true, message: 'Please input or select API provider URL!' },
104
- { type: 'url', message: 'Please enter a valid URL!' }
105
- ]}
106
- >
107
- <AutoComplete
108
- options={getBaseURLOptions()}
109
- placeholder='Enter or select API provider URL'
110
- filterOption={filter}
111
- onChange={handleChange}
112
- allowClear
113
- />
102
+ <Form.Item label='API URL' required>
103
+ <Space.Compact block>
104
+ <Form.Item
105
+ label='API URL'
106
+ name='baseURLAI'
107
+ noStyle
108
+ rules={[
109
+ { required: true, message: 'Please input or select API provider URL!' },
110
+ { type: 'url', message: 'Please enter a valid URL!' }
111
+ ]}
112
+ >
113
+ <AutoComplete
114
+ options={getBaseURLOptions()}
115
+ placeholder='Enter or select API provider URL'
116
+ filterOption={filter}
117
+ onChange={handleChange}
118
+ allowClear
119
+ style={{ width: '75%' }}
120
+ />
121
+ </Form.Item>
122
+ <Form.Item
123
+ label='API PATH'
124
+ name='apiPathAI'
125
+ noStyle
126
+ >
127
+ <Input
128
+ placeholder='Enter API path'
129
+ style={{ width: '25%' }}
130
+ />
131
+ </Form.Item>
132
+ </Space.Compact>
114
133
  </Form.Item>
115
-
116
134
  <Form.Item
117
135
  label={e('modelAi')}
118
136
  name='modelAI'
@@ -145,6 +163,18 @@ export default function AIConfigForm ({ initialValues, onSubmit, showAIConfig })
145
163
  </AutoComplete>
146
164
  </Form.Item>
147
165
 
166
+ <Form.Item
167
+ label={e('language')}
168
+ name='languageAI'
169
+ rules={[{ required: true, message: 'Please input language' }]}
170
+ >
171
+ <AutoComplete options={defaultLangs} placement='topLeft'>
172
+ <Input
173
+ placeholder={e('language')}
174
+ />
175
+ </AutoComplete>
176
+ </Form.Item>
177
+
148
178
  <Form.Item>
149
179
  <Button type='primary' htmlType='submit'>
150
180
  {e('save')}
@@ -7,12 +7,14 @@ import {
7
7
  Input,
8
8
  Form,
9
9
  InputNumber,
10
- TreeSelect
10
+ TreeSelect,
11
+ Alert
11
12
  } from 'antd'
12
13
  import { formItemLayout } from '../../common/form-layout'
13
14
  import {
14
15
  newBookmarkIdPrefix,
15
- terminalRdpType
16
+ terminalRdpType,
17
+ rdpWikiLink
16
18
  } from '../../common/constants'
17
19
  import useSubmit from './use-submit'
18
20
  import copy from 'json-deep-copy'
@@ -22,6 +24,7 @@ import { getRandomDefaultColor } from '../../common/rand-hex-color.js'
22
24
  import formatBookmarkGroups from './bookmark-group-tree-format'
23
25
  import findBookmarkGroupId from '../../common/find-bookmark-group-id'
24
26
  import ProfileItem from './profile-form-item'
27
+ import Link from '../common/external-link'
25
28
 
26
29
  const FormItem = Form.Item
27
30
  const e = window.translate
@@ -63,8 +66,18 @@ export default function RdpFormUi (props) {
63
66
  bookmarkGroups = []
64
67
  } = props
65
68
  const tree = formatBookmarkGroups(bookmarkGroups)
69
+ const alertProps = {
70
+ message: (
71
+ <Link to={rdpWikiLink}>WIKI: {rdpWikiLink}</Link>
72
+ ),
73
+ type: 'warning',
74
+ className: 'mg2y'
75
+ }
66
76
  return (
67
77
  <div className='pd1x'>
78
+ <Alert
79
+ {...alertProps}
80
+ />
68
81
  <FormItem
69
82
  {...formItemLayout}
70
83
  label={e('title')}
@@ -0,0 +1,42 @@
1
+ import React from 'react'
2
+ import writeEmitter from 'manate/events/write-emitter'
3
+ import { run } from 'manate/utils'
4
+
5
+ export class Component extends React.Component {
6
+ constructor (props) {
7
+ super(props)
8
+ this.isTrigger = null
9
+ this.originalRender = this.render
10
+ this.render = this.autoRender
11
+
12
+ const originalDidMount = this.componentDidMount
13
+ this.componentDidMount = () => {
14
+ writeEmitter.on(this.handleWrite)
15
+ if (originalDidMount) {
16
+ originalDidMount.call(this)
17
+ }
18
+ }
19
+
20
+ const originalWillUnmount = this.componentWillUnmount
21
+ this.componentWillUnmount = () => {
22
+ writeEmitter.off(this.handleWrite)
23
+ if (originalWillUnmount) {
24
+ originalWillUnmount.call(this)
25
+ }
26
+ }
27
+ }
28
+
29
+ handleWrite = (writeLog) => {
30
+ if (this.isTrigger && this.isTrigger(writeLog)) {
31
+ this.forceUpdate()
32
+ }
33
+ }
34
+
35
+ autoRender = () => {
36
+ const [element, isTrigger] = run(() => {
37
+ return this.originalRender()
38
+ })
39
+ this.isTrigger = isTrigger
40
+ return element
41
+ }
42
+ }
@@ -6,17 +6,21 @@ import {
6
6
  export default function InputAutoFocus (props) {
7
7
  const { type, selectall = false, ...rest } = props
8
8
  const inputRef = useRef(null)
9
+ const isFirstRender = useRef(true)
9
10
 
10
11
  useEffect(() => {
11
12
  if (inputRef.current) {
12
13
  const { value } = props
13
- if (value && selectall) {
14
+ if (value && selectall && isFirstRender.current) {
15
+ inputRef.current.focus()
14
16
  inputRef.current.setSelectionRange(0, value.length)
17
+ isFirstRender.current = false
15
18
  } else {
16
19
  inputRef.current.focus()
17
20
  }
18
21
  }
19
- }, [props.value, props.selectall]) // Focus when these props change
22
+ }, [props.value, props.selectall])
23
+
20
24
  let InputComponent
21
25
  switch (type) {
22
26
  case 'password':
@@ -25,11 +29,11 @@ export default function InputAutoFocus (props) {
25
29
  default:
26
30
  InputComponent = Input
27
31
  }
32
+
28
33
  return (
29
34
  <InputComponent
30
35
  ref={inputRef}
31
36
  {...rest}
32
37
  />
33
-
34
38
  )
35
39
  }
@@ -13,7 +13,6 @@ import Footer from '../footer/footer-entry'
13
13
  import SessionsWrap from '../session/sessions'
14
14
  import QuickCommandsFooterBox from '../quick-commands/quick-commands-box'
15
15
  import pixed from './pixed'
16
- import copy from 'json-deep-copy'
17
16
  import { pick } from 'lodash-es'
18
17
  import './layout.styl'
19
18
 
@@ -181,7 +180,7 @@ export default auto(function Layout (props) {
181
180
  'openedSideBar',
182
181
  'config'
183
182
  ]),
184
- tabs: copy(store.tabs)
183
+ tabs: store.tabs
185
184
  }
186
185
  return [
187
186
  <Layouts {...layoutProps} key='layouts'>
@@ -1,11 +1,10 @@
1
- import { memo } from 'react'
2
1
  import {
3
2
  splitConfig
4
3
  } from '../../common/constants'
5
4
  import LayoutItem from './layout-item'
6
5
  import pixed from './pixed'
7
6
 
8
- export default memo(function LayoutWrap (props) {
7
+ export default function LayoutWrap (props) {
9
8
  const {
10
9
  children,
11
10
  layout,
@@ -61,4 +60,4 @@ export default memo(function LayoutWrap (props) {
61
60
  }
62
61
  </div>
63
62
  )
64
- })
63
+ }
@@ -165,7 +165,7 @@ export default auto(function Index (props) {
165
165
  'commandLineHelp'
166
166
  ]),
167
167
  installSrc,
168
- upgradeInfo
168
+ upgradeInfo: store.upgradeInfo
169
169
  }
170
170
  const conflictStoreProps = {
171
171
  fileTransferChanged: JSON.stringify(copiedTransfer),
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * terminal/sftp wrapper
3
3
  */
4
- import { Component, createRef } from 'react'
4
+ import { createRef } from 'react'
5
+ import { Component } from '../common/component'
5
6
  import Term from '../terminal/terminal.jsx'
6
7
  import Sftp from '../sftp/sftp-entry'
7
8
  import RdpSession from '../rdp/rdp-session'
@@ -1,4 +1,4 @@
1
- import { Component } from 'react'
1
+ import { Component } from '../common/component'
2
2
  import Session from './session.jsx'
3
3
 
4
4
  import { pick } from 'lodash-es'
@@ -15,7 +15,6 @@ import TabQuickCommands from './tab-quick-commands'
15
15
  import TabSettings from './tab-settings'
16
16
  import TabThemes from './tab-themes'
17
17
  import TabProfiles from './tab-profiles'
18
- import deepCopy from 'json-deep-copy'
19
18
 
20
19
  const e = window.translate
21
20
 
@@ -39,8 +38,7 @@ export default auto(function SettingModalWrap (props) {
39
38
  shouldConfirmDel: tabsShouldConfirmDel.includes(settingTab),
40
39
  list: settingSidebarList
41
40
  }
42
- const bookmarks = deepCopy(store.bookmarks)
43
- const bookmarkGroups = deepCopy(store.bookmarkGroups)
41
+ const { bookmarks, bookmarkGroups } = store
44
42
  const formProps = {
45
43
  store,
46
44
  formData: settingItem,
@@ -69,7 +69,7 @@ export default class SettingTerminal extends Component {
69
69
  if (name === 'useSystemTitleBar') {
70
70
  message.info(e('useSystemTitleBarTip'), 8)
71
71
  } else if (name === 'sftpPathFollowSsh' && value) {
72
- message.warn(e('sftpPathFollowSshTip'), 8)
72
+ message.warning(e('sftpPathFollowSshTip'), 8)
73
73
  }
74
74
  this.saveConfig({
75
75
  [name]: value
@@ -4,7 +4,6 @@
4
4
 
5
5
  import { auto } from 'manate/react'
6
6
  import TreeList from '../tree-list/tree-list'
7
- import deepCopy from 'json-deep-copy'
8
7
 
9
8
  export default auto(function BookmarkSelect (props) {
10
9
  const { store, from } = props
@@ -25,7 +24,7 @@ export default auto(function BookmarkSelect (props) {
25
24
  store.onSelectBookmark(item.id)
26
25
  }
27
26
  const base = {
28
- bookmarks: deepCopy(bookmarks || []),
27
+ bookmarks: bookmarks || [],
29
28
  type: 'bookmarks',
30
29
  onClickItem,
31
30
  listStyle,
@@ -34,7 +33,7 @@ export default auto(function BookmarkSelect (props) {
34
33
  const propsTree = {
35
34
  ...base,
36
35
  shouldConfirmDel: true,
37
- bookmarkGroups: deepCopy(store.getBookmarkGroupsTotal()),
36
+ bookmarkGroups: store.getBookmarkGroupsTotal(),
38
37
  expandedKeys,
39
38
  leftSidebarWidth,
40
39
  bookmarkGroupTree: store.bookmarkGroupTree
@@ -1,4 +1,3 @@
1
- import { memo } from 'react'
2
1
  import {
3
2
  GithubOutlined,
4
3
  GlobalOutlined,
@@ -16,6 +15,7 @@ import { Modal, Tabs, Button } from 'antd'
16
15
  import Link from '../common/external-link'
17
16
  import LogoElem from '../common/logo-elem'
18
17
  import RunningTime from './app-running-time'
18
+ import { auto } from 'manate/react'
19
19
 
20
20
  import {
21
21
  packInfo,
@@ -26,7 +26,7 @@ import './info.styl'
26
26
 
27
27
  const e = window.translate
28
28
 
29
- export default memo(function InfoModal (props) {
29
+ export default auto(function InfoModal (props) {
30
30
  const handleChangeTab = key => {
31
31
  window.store.infoModalTab = key
32
32
  }
@@ -2,6 +2,7 @@
2
2
  * session tabs component
3
3
  */
4
4
 
5
+ import { Component } from '../common/component'
5
6
  import React from 'react'
6
7
  import runIdle from '../../common/run-idle'
7
8
  import { throttle } from 'lodash-es'
@@ -43,7 +44,7 @@ import classNames from 'classnames'
43
44
 
44
45
  const e = window.translate
45
46
 
46
- export default class Tabs extends React.Component {
47
+ export default class Tabs extends Component {
47
48
  constructor (props) {
48
49
  super(props)
49
50
  this.tabsRef = React.createRef()
@@ -2,7 +2,8 @@
2
2
  * file section
3
3
  */
4
4
 
5
- import { Component, createRef } from 'react'
5
+ import { createRef } from 'react'
6
+ import { Component } from '../common/component'
6
7
  import { refs } from '../common/ref'
7
8
  import {
8
9
  CloseOutlined,
@@ -2,7 +2,7 @@
2
2
  * tree list for bookmarks
3
3
  */
4
4
 
5
- import { Component } from 'react'
5
+ import { Component } from '../common/component'
6
6
  import {
7
7
  CheckOutlined,
8
8
  CloseOutlined,
@@ -15,6 +15,7 @@ import {
15
15
  import * as ls from '../common/safe-local-storage'
16
16
  import { refs, refsStatic } from '../components/common/ref'
17
17
  import { action } from 'manate'
18
+ import deepCopy from 'json-deep-copy'
18
19
 
19
20
  const e = window.translate
20
21
  const { assign } = Object
@@ -192,10 +193,11 @@ export default Store => {
192
193
  if (!profile || authType !== 'profiles') {
193
194
  return tab
194
195
  }
195
- const p = window.store.profiles.find(x => x.id === profile)
196
+ let p = window.store.profiles.find(x => x.id === profile)
196
197
  if (!p) {
197
198
  return tab
198
199
  }
200
+ p = deepCopy(p)
199
201
  // delete tab.password
200
202
  // delete tab.privateKey
201
203
  // delete tab.passphrase
@@ -279,4 +281,26 @@ export default Store => {
279
281
  Store.prototype.getLangNames = function () {
280
282
  return window.et.langs.map(d => d.name)
281
283
  }
284
+
285
+ Store.prototype.fixProfiles = function () {
286
+ const { profiles } = window.store
287
+ const len = profiles.length
288
+ let i = len - 1
289
+ for (;i >= 0; i--) {
290
+ const f = profiles[i]
291
+ if (f.name) {
292
+ continue
293
+ }
294
+ let count = 0
295
+ let id = 'PROFILE' + i
296
+ while (profiles.find(d => d.id === id)) {
297
+ count = count + 1
298
+ id = 'PROFILE' + count
299
+ }
300
+ const np = deepCopy(f)
301
+ np.id = id
302
+ np.name = id
303
+ profiles[i] = np
304
+ }
305
+ }
282
306
  }
@@ -194,6 +194,7 @@ export default (Store) => {
194
194
  ext.lastDataUpdateTime = await getData('lastDataUpdateTime') || 0
195
195
  Object.assign(store, ext)
196
196
  await store.fixBookmarkGroups()
197
+ await store.fixProfiles()
197
198
 
198
199
  store.checkDefaultTheme()
199
200
  store.loadFontList()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "1.60.32",
3
+ "version": "1.60.46",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",