@electerm/electerm-react 1.60.6 → 1.60.18
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/components/ai/ai-chat.jsx +9 -1
- package/client/components/ai/ai-config.jsx +13 -1
- package/client/components/ai/ai-output.jsx +32 -37
- package/client/components/ai/ai.styl +1 -0
- package/client/components/ai/get-brand.js +34 -0
- package/client/components/bookmark-form/rdp-form-ui.jsx +1 -1
- package/client/components/main/main.jsx +7 -3
- package/client/components/side-panel-r/right-side-panel.styl +6 -7
- package/client/components/side-panel-r/side-panel-r.jsx +32 -10
- package/client/components/sidebar/history-item.jsx +20 -3
- package/client/components/sidebar/history.jsx +4 -1
- package/client/components/tabs/index.jsx +8 -26
- package/client/components/tabs/no-session.jsx +40 -0
- package/client/components/tabs/tabs.styl +6 -0
- package/client/components/terminal/index.jsx +20 -12
- package/client/store/store.js +15 -0
- package/client/store/sync.js +4 -1
- package/client/store/tab.js +2 -1
- package/package.json +1 -1
|
@@ -307,6 +307,7 @@ export const batchOpHelpLink = 'https://github.com/electerm/electerm/wiki/batch-
|
|
|
307
307
|
export const proxyHelpLink = 'https://github.com/electerm/electerm/wiki/proxy-format'
|
|
308
308
|
export const regexHelpLink = 'https://github.com/electerm/electerm/wiki/Terminal-keywords-highlight-regular-expression-exmaples'
|
|
309
309
|
export const connectionHoppingWikiLink = 'https://github.com/electerm/electerm/wiki/Connection-Hopping-Behavior-Change-in-electerm-since-v1.50.65'
|
|
310
|
+
export const aiConfigWikiLink = 'https://github.com/electerm/electerm/wiki/AI-model-config-guide'
|
|
310
311
|
export const modals = {
|
|
311
312
|
hide: 0,
|
|
312
313
|
setting: 1,
|
|
@@ -11,6 +11,10 @@ import {
|
|
|
11
11
|
SendOutlined,
|
|
12
12
|
UnorderedListOutlined
|
|
13
13
|
} from '@ant-design/icons'
|
|
14
|
+
import {
|
|
15
|
+
aiConfigWikiLink
|
|
16
|
+
} from '../../common/constants'
|
|
17
|
+
import HelpIcon from '../common/help-icon'
|
|
14
18
|
import './ai.styl'
|
|
15
19
|
|
|
16
20
|
const { TextArea } = Input
|
|
@@ -152,7 +156,11 @@ export default function AIChat (props) {
|
|
|
152
156
|
/>
|
|
153
157
|
<UnorderedListOutlined
|
|
154
158
|
onClick={clearHistory}
|
|
155
|
-
className='
|
|
159
|
+
className='mg2x pointer clear-ai-icon icon-hover'
|
|
160
|
+
title='Clear AI chat history'
|
|
161
|
+
/>
|
|
162
|
+
<HelpIcon
|
|
163
|
+
link={aiConfigWikiLink}
|
|
156
164
|
/>
|
|
157
165
|
{renderConfig()}
|
|
158
166
|
</Flex>
|
|
@@ -3,9 +3,14 @@ import {
|
|
|
3
3
|
Input,
|
|
4
4
|
Button,
|
|
5
5
|
AutoComplete,
|
|
6
|
-
Modal
|
|
6
|
+
Modal,
|
|
7
|
+
Alert
|
|
7
8
|
} from 'antd'
|
|
8
9
|
import { useEffect, useState } from 'react'
|
|
10
|
+
import Link from '../common/external-link'
|
|
11
|
+
import {
|
|
12
|
+
aiConfigWikiLink
|
|
13
|
+
} from '../../common/constants'
|
|
9
14
|
|
|
10
15
|
// Comprehensive API provider configurations
|
|
11
16
|
import providers from './providers'
|
|
@@ -70,6 +75,13 @@ export default function AIConfigForm ({ initialValues, onSubmit, showAIConfig })
|
|
|
70
75
|
onCancel={handleCancel}
|
|
71
76
|
footer={null}
|
|
72
77
|
>
|
|
78
|
+
<Alert
|
|
79
|
+
message={
|
|
80
|
+
<Link to={aiConfigWikiLink}>WIKI: {aiConfigWikiLink}</Link>
|
|
81
|
+
}
|
|
82
|
+
type='info'
|
|
83
|
+
className='mg2y'
|
|
84
|
+
/>
|
|
73
85
|
<Form
|
|
74
86
|
form={form}
|
|
75
87
|
onFinish={handleSubmit}
|
|
@@ -1,47 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useState, useMemo } from 'react'
|
|
2
2
|
import ReactMarkdown from 'react-markdown'
|
|
3
3
|
import { copy } from '../../common/clipboard'
|
|
4
4
|
import Link from '../common/external-link'
|
|
5
5
|
import { Tag } from 'antd'
|
|
6
|
-
import { CopyOutlined, PlayCircleOutlined } from '@ant-design/icons'
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
function getBrand (baseURLAI) {
|
|
10
|
-
// First, try to match with providers
|
|
11
|
-
const provider = providers.find(p => p.baseURL === baseURLAI)
|
|
12
|
-
if (provider) {
|
|
13
|
-
return {
|
|
14
|
-
brand: provider.label,
|
|
15
|
-
brandUrl: provider.homepage
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// If no match, extract brand from URL
|
|
20
|
-
try {
|
|
21
|
-
const url = new URL(baseURLAI)
|
|
22
|
-
const hostname = url.hostname
|
|
23
|
-
const parts = hostname.split('.')
|
|
24
|
-
let brand = parts[parts.length - 2] // Usually the brand name is the second-to-last part
|
|
25
|
-
|
|
26
|
-
// Capitalize the first letter
|
|
27
|
-
brand = brand.charAt(0).toUpperCase() + brand.slice(1)
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
brand,
|
|
31
|
-
brandUrl: `https://${parts[parts.length - 2]}.${parts[parts.length - 1]}`
|
|
32
|
-
}
|
|
33
|
-
} catch (error) {
|
|
34
|
-
// If URL parsing fails, return null
|
|
35
|
-
return {
|
|
36
|
-
brand: null,
|
|
37
|
-
brandUrl: null
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
6
|
+
import { CopyOutlined, PlayCircleOutlined, DownCircleOutlined } from '@ant-design/icons'
|
|
7
|
+
import getBrand from './get-brand'
|
|
41
8
|
|
|
42
9
|
const e = window.translate
|
|
43
10
|
|
|
44
11
|
export default function AIOutput ({ item }) {
|
|
12
|
+
const [showFull, setShowFull] = useState(false)
|
|
45
13
|
const {
|
|
46
14
|
response,
|
|
47
15
|
baseURLAI
|
|
@@ -52,6 +20,18 @@ export default function AIOutput ({ item }) {
|
|
|
52
20
|
|
|
53
21
|
const { brand, brandUrl } = getBrand(baseURLAI)
|
|
54
22
|
|
|
23
|
+
const truncatedResponse = useMemo(() => {
|
|
24
|
+
if (!response) return ''
|
|
25
|
+
const codeBlockRegex = /```[\s\S]*?```/
|
|
26
|
+
const match = response.match(codeBlockRegex)
|
|
27
|
+
if (match) {
|
|
28
|
+
const index = match.index + match[0].length
|
|
29
|
+
return response.slice(0, index) + '\n\n... ...'
|
|
30
|
+
}
|
|
31
|
+
// If no code block found, show first 5 lines
|
|
32
|
+
return response.split('\n').slice(0, 5).join('\n') + '\n\n... ...'
|
|
33
|
+
}, [response])
|
|
34
|
+
|
|
55
35
|
const renderCode = (props) => {
|
|
56
36
|
const { node, className = '', children, ...rest } = props
|
|
57
37
|
const code = String(children).replace(/\n$/, '')
|
|
@@ -107,8 +87,22 @@ export default function AIOutput ({ item }) {
|
|
|
107
87
|
)
|
|
108
88
|
}
|
|
109
89
|
|
|
90
|
+
function renderShowMore () {
|
|
91
|
+
if (showFull) {
|
|
92
|
+
return null
|
|
93
|
+
}
|
|
94
|
+
return (
|
|
95
|
+
<span
|
|
96
|
+
onClick={() => setShowFull(true)}
|
|
97
|
+
className='mg1t pointer'
|
|
98
|
+
>
|
|
99
|
+
<DownCircleOutlined /> {e('fullContent')}
|
|
100
|
+
</span>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
110
104
|
const mdProps = {
|
|
111
|
-
children: response,
|
|
105
|
+
children: showFull ? response : truncatedResponse,
|
|
112
106
|
components: {
|
|
113
107
|
code: renderCode
|
|
114
108
|
}
|
|
@@ -118,6 +112,7 @@ export default function AIOutput ({ item }) {
|
|
|
118
112
|
<div className='pd1'>
|
|
119
113
|
{renderBrand()}
|
|
120
114
|
<ReactMarkdown {...mdProps} />
|
|
115
|
+
{renderShowMore()}
|
|
121
116
|
</div>
|
|
122
117
|
)
|
|
123
118
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import providers from './providers'
|
|
2
|
+
|
|
3
|
+
export default function getBrand (baseURLAI) {
|
|
4
|
+
// First, try to match with providers
|
|
5
|
+
const provider = providers.find(p => p.baseURL === baseURLAI)
|
|
6
|
+
if (provider) {
|
|
7
|
+
return {
|
|
8
|
+
brand: provider.label,
|
|
9
|
+
brandUrl: provider.homepage
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// If no match, extract brand from URL
|
|
14
|
+
try {
|
|
15
|
+
const url = new URL(baseURLAI)
|
|
16
|
+
const hostname = url.hostname
|
|
17
|
+
const parts = hostname.split('.')
|
|
18
|
+
let brand = parts[parts.length - 2] // Usually the brand name is the second-to-last part
|
|
19
|
+
|
|
20
|
+
// Capitalize the first letter
|
|
21
|
+
brand = brand.charAt(0).toUpperCase() + brand.slice(1)
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
brand,
|
|
25
|
+
brandUrl: `https://${parts[parts.length - 2]}.${parts[parts.length - 1]}`
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
// If URL parsing fails, return null
|
|
29
|
+
return {
|
|
30
|
+
brand: null,
|
|
31
|
+
brandUrl: null
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -99,7 +99,9 @@ export default auto(function Index (props) {
|
|
|
99
99
|
uiThemeConfig,
|
|
100
100
|
transferHistory,
|
|
101
101
|
transferToConfirm,
|
|
102
|
-
openResolutionEdit
|
|
102
|
+
openResolutionEdit,
|
|
103
|
+
rightPanelTitle,
|
|
104
|
+
rightPanelTab
|
|
103
105
|
} = store
|
|
104
106
|
const upgradeInfo = deepCopy(store.upgradeInfo)
|
|
105
107
|
const cls = classnames({
|
|
@@ -191,7 +193,9 @@ export default auto(function Index (props) {
|
|
|
191
193
|
const rightPanelProps = {
|
|
192
194
|
rightPanelVisible: store.rightPanelVisible,
|
|
193
195
|
rightPanelPinned: store.rightPanelPinned,
|
|
194
|
-
rightPanelWidth: store.rightPanelWidth
|
|
196
|
+
rightPanelWidth: store.rightPanelWidth,
|
|
197
|
+
title: rightPanelTitle,
|
|
198
|
+
rightPanelTab
|
|
195
199
|
}
|
|
196
200
|
const terminalInfoProps = {
|
|
197
201
|
...deepCopy(store.terminalInfoProps),
|
|
@@ -222,7 +226,7 @@ export default auto(function Index (props) {
|
|
|
222
226
|
activeTabId: store.activeTabId,
|
|
223
227
|
showAIConfig: store.showAIConfig
|
|
224
228
|
}
|
|
225
|
-
const rightPanelContent =
|
|
229
|
+
const rightPanelContent = rightPanelTab === 'ai'
|
|
226
230
|
? <AIChat {...aiChatProps} />
|
|
227
231
|
: <TerminalInfo {...terminalInfoProps} />
|
|
228
232
|
return (
|
|
@@ -3,29 +3,28 @@
|
|
|
3
3
|
.right-side-panel
|
|
4
4
|
position absolute
|
|
5
5
|
right 0
|
|
6
|
-
top
|
|
6
|
+
top 36px
|
|
7
7
|
bottom 0
|
|
8
8
|
z-index 200
|
|
9
9
|
background main
|
|
10
10
|
color text-light
|
|
11
11
|
border-left 1px solid darken(main, 30%)
|
|
12
|
+
&.right-side-panel-pinned
|
|
13
|
+
top 0
|
|
12
14
|
|
|
13
15
|
.drag-handle
|
|
14
16
|
left 0
|
|
15
17
|
right auto
|
|
16
18
|
display block
|
|
17
|
-
|
|
19
|
+
.right-panel-title
|
|
20
|
+
border-bottom 1px solid darken(main, 30%)
|
|
18
21
|
.right-side-panel-controls
|
|
19
|
-
position absolute
|
|
20
|
-
left 10px
|
|
21
|
-
top 10px
|
|
22
22
|
color text-dark
|
|
23
23
|
font-size 16px
|
|
24
24
|
&:hover
|
|
25
25
|
color text
|
|
26
26
|
cursor pointer
|
|
27
27
|
&.right-side-panel-pin
|
|
28
|
-
left 30px
|
|
29
28
|
&.pinned
|
|
30
29
|
color success
|
|
31
30
|
.right-side-panel-content
|
|
@@ -33,7 +32,7 @@
|
|
|
33
32
|
overflow-y scroll
|
|
34
33
|
position absolute
|
|
35
34
|
left 0
|
|
36
|
-
top
|
|
35
|
+
top 55px
|
|
37
36
|
right 0
|
|
38
37
|
bottom 0
|
|
39
38
|
.animate-fast
|
|
@@ -3,20 +3,31 @@ import DragHandle from '../common/drag-handle'
|
|
|
3
3
|
import './right-side-panel.styl'
|
|
4
4
|
import {
|
|
5
5
|
CloseCircleOutlined,
|
|
6
|
-
PushpinOutlined
|
|
6
|
+
PushpinOutlined,
|
|
7
|
+
InfoCircleOutlined
|
|
7
8
|
} from '@ant-design/icons'
|
|
9
|
+
import {
|
|
10
|
+
Typography,
|
|
11
|
+
Flex,
|
|
12
|
+
Tag
|
|
13
|
+
} from 'antd'
|
|
8
14
|
|
|
9
15
|
export default memo(function RightSidePanel (
|
|
10
16
|
{
|
|
11
17
|
rightPanelVisible,
|
|
12
18
|
rightPanelPinned,
|
|
13
19
|
rightPanelWidth,
|
|
14
|
-
children
|
|
20
|
+
children,
|
|
21
|
+
title,
|
|
22
|
+
rightPanelTab
|
|
15
23
|
}
|
|
16
24
|
) {
|
|
17
25
|
if (!rightPanelVisible) {
|
|
18
26
|
return null
|
|
19
27
|
}
|
|
28
|
+
const tag = rightPanelTab === 'ai'
|
|
29
|
+
? <Tag className='mg1r'>AI</Tag>
|
|
30
|
+
: <InfoCircleOutlined className='mg1r' />
|
|
20
31
|
|
|
21
32
|
function onDragEnd (nw) {
|
|
22
33
|
window.store.setRightSidePanelWidth(nw)
|
|
@@ -36,7 +47,7 @@ export default memo(function RightSidePanel (
|
|
|
36
47
|
}
|
|
37
48
|
|
|
38
49
|
const panelProps = {
|
|
39
|
-
className: 'right-side-panel animate-fast',
|
|
50
|
+
className: 'right-side-panel animate-fast' + (rightPanelPinned ? ' right-side-panel-pinned' : ''),
|
|
40
51
|
id: 'right-side-panel',
|
|
41
52
|
style: {
|
|
42
53
|
width: `${rightPanelWidth}px`
|
|
@@ -60,13 +71,24 @@ export default memo(function RightSidePanel (
|
|
|
60
71
|
{...panelProps}
|
|
61
72
|
>
|
|
62
73
|
<DragHandle {...dragProps} />
|
|
63
|
-
<
|
|
64
|
-
className='right-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
{
|
|
69
|
-
|
|
74
|
+
<Flex
|
|
75
|
+
className='right-panel-title pd2'
|
|
76
|
+
justify='space-between'
|
|
77
|
+
align='center'
|
|
78
|
+
>
|
|
79
|
+
<Typography.Text level={4} ellipsis style={{ margin: 0, flex: 1 }}>
|
|
80
|
+
{tag} {title}
|
|
81
|
+
</Typography.Text>
|
|
82
|
+
<Flex>
|
|
83
|
+
<PushpinOutlined
|
|
84
|
+
{...pinProps}
|
|
85
|
+
/>
|
|
86
|
+
<CloseCircleOutlined
|
|
87
|
+
className='right-side-panel-close right-side-panel-controls mg1l'
|
|
88
|
+
onClick={onClose}
|
|
89
|
+
/>
|
|
90
|
+
</Flex>
|
|
91
|
+
</Flex>
|
|
70
92
|
<div className='right-side-panel-content'>
|
|
71
93
|
{children}
|
|
72
94
|
</div>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useRef } from 'react'
|
|
1
2
|
import createTitle, { createTitleWithTag } from '../../common/create-title'
|
|
2
3
|
import { DeleteOutlined } from '@ant-design/icons'
|
|
3
4
|
|
|
@@ -7,9 +8,25 @@ export default function HistoryItem (props) {
|
|
|
7
8
|
item,
|
|
8
9
|
index
|
|
9
10
|
} = props
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
const timeoutRef = useRef(null)
|
|
12
|
+
|
|
13
|
+
const handleClick = useCallback(() => {
|
|
14
|
+
if (timeoutRef.current) {
|
|
15
|
+
clearTimeout(timeoutRef.current)
|
|
16
|
+
}
|
|
17
|
+
timeoutRef.current = setTimeout(() => {
|
|
18
|
+
store.onSelectHistory(item.tab)
|
|
19
|
+
}, 10)
|
|
20
|
+
}, [item.tab])
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
return () => {
|
|
24
|
+
if (timeoutRef.current) {
|
|
25
|
+
clearTimeout(timeoutRef.current)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}, [])
|
|
29
|
+
|
|
13
30
|
function handleDelete (e) {
|
|
14
31
|
e.stopPropagation()
|
|
15
32
|
store.history.splice(index, 1)
|
|
@@ -10,13 +10,16 @@ export default auto(function HistoryPanel (props) {
|
|
|
10
10
|
const {
|
|
11
11
|
history
|
|
12
12
|
} = store
|
|
13
|
+
const arr = props.sort
|
|
14
|
+
? [...history].sort((a, b) => { return b.count - a.count })
|
|
15
|
+
: history
|
|
13
16
|
return (
|
|
14
17
|
<div
|
|
15
18
|
className='sidebar-panel-history'
|
|
16
19
|
>
|
|
17
20
|
<div className='pd2x'>
|
|
18
21
|
{
|
|
19
|
-
|
|
22
|
+
arr.map((item, i) => {
|
|
20
23
|
return (
|
|
21
24
|
<HistoryItem
|
|
22
25
|
key={item.id}
|
|
@@ -24,9 +24,8 @@ import {
|
|
|
24
24
|
TwoRowsRightIcon,
|
|
25
25
|
TwoColumnsBottomIcon
|
|
26
26
|
} from '../icons/split-icons'
|
|
27
|
-
import { Dropdown, Popover
|
|
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
|
|
@@ -548,31 +548,13 @@ export default class Tabs extends React.Component {
|
|
|
548
548
|
}
|
|
549
549
|
|
|
550
550
|
renderNoSession = () => {
|
|
551
|
-
const props = {
|
|
552
|
-
style: {
|
|
553
|
-
height: this.props.height + 'px'
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
551
|
return (
|
|
557
|
-
<
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
{e('newTab')}
|
|
564
|
-
</Button>
|
|
565
|
-
<Button
|
|
566
|
-
onClick={this.handleNewSsh}
|
|
567
|
-
size='large'
|
|
568
|
-
className='mg1r mg1b'
|
|
569
|
-
>
|
|
570
|
-
{e('newBookmark')}
|
|
571
|
-
</Button>
|
|
572
|
-
<div className='pd3'>
|
|
573
|
-
<LogoElem />
|
|
574
|
-
</div>
|
|
575
|
-
</div>
|
|
552
|
+
<NoSession
|
|
553
|
+
height={this.props.height}
|
|
554
|
+
onNewTab={this.handleNewTab}
|
|
555
|
+
onNewSsh={this.handleNewSsh}
|
|
556
|
+
batch={this.props.batch}
|
|
557
|
+
/>
|
|
576
558
|
)
|
|
577
559
|
}
|
|
578
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
|
+
}
|
|
@@ -1110,6 +1110,22 @@ clear\r`
|
|
|
1110
1110
|
this.bufferMode = buf.type
|
|
1111
1111
|
}
|
|
1112
1112
|
|
|
1113
|
+
buildWsUrl = () => {
|
|
1114
|
+
const { host, port, tokenElecterm } = this.props.config
|
|
1115
|
+
const { id } = this.props.tab
|
|
1116
|
+
if (window.et.buildWsUrl) {
|
|
1117
|
+
return window.et.buildWsUrl(
|
|
1118
|
+
host,
|
|
1119
|
+
port,
|
|
1120
|
+
tokenElecterm,
|
|
1121
|
+
id,
|
|
1122
|
+
this.props.sessionId
|
|
1123
|
+
)
|
|
1124
|
+
}
|
|
1125
|
+
const wsUrl = `ws://${host}:${port}/terminals/${id}?sessionId=${this.props.sessionId}&token=${tokenElecterm}`
|
|
1126
|
+
return wsUrl
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1113
1129
|
remoteInit = async (term = this.term) => {
|
|
1114
1130
|
this.setState({
|
|
1115
1131
|
loading: true
|
|
@@ -1117,11 +1133,7 @@ clear\r`
|
|
|
1117
1133
|
const { cols, rows } = term
|
|
1118
1134
|
const { config } = this.props
|
|
1119
1135
|
const {
|
|
1120
|
-
|
|
1121
|
-
port,
|
|
1122
|
-
tokenElecterm,
|
|
1123
|
-
keywords = [],
|
|
1124
|
-
server = ''
|
|
1136
|
+
keywords = []
|
|
1125
1137
|
} = config
|
|
1126
1138
|
const { sessionId, logName } = this.props
|
|
1127
1139
|
const tab = window.store.applyProfileToTabs(deepCopy(this.props.tab || {}))
|
|
@@ -1189,11 +1201,7 @@ clear\r`
|
|
|
1189
1201
|
this.setStatus(statusMap.success)
|
|
1190
1202
|
term.pid = id
|
|
1191
1203
|
this.pid = id
|
|
1192
|
-
const
|
|
1193
|
-
? server.replace(/https?:\/\//, '')
|
|
1194
|
-
: `${host}:${port}`
|
|
1195
|
-
const pre = server.startsWith('https') ? 'wss' : 'ws'
|
|
1196
|
-
const wsUrl = `${pre}://${hs}/terminals/${id}?sessionId=${sessionId}&token=${tokenElecterm}`
|
|
1204
|
+
const wsUrl = this.buildWsUrl()
|
|
1197
1205
|
const socket = new WebSocket(wsUrl)
|
|
1198
1206
|
socket.onclose = this.oncloseSocket
|
|
1199
1207
|
socket.onerror = this.onerrorSocket
|
|
@@ -1306,10 +1314,10 @@ clear\r`
|
|
|
1306
1314
|
}
|
|
1307
1315
|
|
|
1308
1316
|
handleShowInfo = () => {
|
|
1309
|
-
const {
|
|
1317
|
+
const { sessionId, logName, tab } = this.props
|
|
1310
1318
|
const infoProps = {
|
|
1311
1319
|
logName,
|
|
1312
|
-
id,
|
|
1320
|
+
id: tab.id,
|
|
1313
1321
|
pid: tab.id,
|
|
1314
1322
|
sessionId,
|
|
1315
1323
|
isRemote: this.isRemote(),
|
package/client/store/store.js
CHANGED
|
@@ -29,6 +29,7 @@ import isColorDark from '../common/is-color-dark'
|
|
|
29
29
|
import { getReverseColor } from '../common/reverse-color'
|
|
30
30
|
import { uniq } from 'lodash-es'
|
|
31
31
|
import deepCopy from 'json-deep-copy'
|
|
32
|
+
import getBrand from '../components/ai/get-brand'
|
|
32
33
|
import {
|
|
33
34
|
settingMap,
|
|
34
35
|
paneMap,
|
|
@@ -38,6 +39,7 @@ import {
|
|
|
38
39
|
terminalSshConfigType
|
|
39
40
|
} from '../common/constants'
|
|
40
41
|
import getInitItem from '../common/init-setting-item'
|
|
42
|
+
import createTitle from '../common/create-title'
|
|
41
43
|
import {
|
|
42
44
|
theme
|
|
43
45
|
} from 'antd'
|
|
@@ -93,6 +95,19 @@ class Store {
|
|
|
93
95
|
return Array.from(window.store._batchInputSelectedTabIds)
|
|
94
96
|
}
|
|
95
97
|
|
|
98
|
+
get rightPanelTitle () {
|
|
99
|
+
const {
|
|
100
|
+
rightPanelTab,
|
|
101
|
+
config: {
|
|
102
|
+
baseURLAI
|
|
103
|
+
}
|
|
104
|
+
} = window.store
|
|
105
|
+
if (rightPanelTab === 'ai') {
|
|
106
|
+
return getBrand(baseURLAI).brand || 'Custom AI Model'
|
|
107
|
+
}
|
|
108
|
+
return createTitle(window.store.currentTab)
|
|
109
|
+
}
|
|
110
|
+
|
|
96
111
|
get inActiveTerminal () {
|
|
97
112
|
const { store } = window
|
|
98
113
|
if (store.showModal) {
|
package/client/store/sync.js
CHANGED
package/client/store/tab.js
CHANGED