@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.
- package/client/common/constants.js +1 -0
- package/client/common/default-setting.js +3 -2
- package/client/components/ai/ai-chat.jsx +3 -1
- package/client/components/ai/ai-config.jsx +47 -17
- package/client/components/bookmark-form/rdp-form-ui.jsx +15 -2
- package/client/components/common/component.jsx +42 -0
- package/client/components/common/input-auto-focus.jsx +7 -3
- package/client/components/layout/layout.jsx +1 -2
- package/client/components/layout/layouts.jsx +2 -3
- package/client/components/main/main.jsx +1 -1
- package/client/components/session/session.jsx +2 -1
- package/client/components/session/sessions.jsx +1 -1
- package/client/components/setting-panel/setting-modal.jsx +1 -3
- package/client/components/setting-panel/setting-terminal.jsx +1 -1
- package/client/components/sidebar/bookmark-select.jsx +2 -3
- package/client/components/sidebar/info-modal.jsx +2 -2
- package/client/components/tabs/index.jsx +2 -1
- package/client/components/tabs/tab.jsx +2 -1
- package/client/components/tree-list/tree-list.jsx +1 -1
- package/client/store/common.js +25 -1
- package/client/store/load-data.js +1 -0
- package/package.json +1 -1
|
@@ -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:
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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])
|
|
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:
|
|
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
|
|
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
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* terminal/sftp wrapper
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
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'
|
|
@@ -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 =
|
|
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.
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
47
|
+
export default class Tabs extends Component {
|
|
47
48
|
constructor (props) {
|
|
48
49
|
super(props)
|
|
49
50
|
this.tabsRef = React.createRef()
|
package/client/store/common.js
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|