@electerm/electerm-react 2.10.27 → 2.11.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.
- package/client/common/default-setting.js +1 -0
- package/client/common/fetch.jsx +1 -1
- package/client/common/has-active-input.js +1 -1
- package/client/common/parse-quick-connect.js +438 -0
- package/client/components/ai/ai-chat-history-item.jsx +1 -1
- package/client/components/ai/ai-config.jsx +1 -1
- package/client/components/bg/css-overwrite.jsx +5 -1
- package/client/components/bookmark-form/ai-bookmark-form.jsx +40 -3
- package/client/components/bookmark-form/bookmark-form.styl +21 -0
- package/client/components/bookmark-form/bookmark-from-history-modal.jsx +141 -0
- package/client/components/bookmark-form/tree-select.jsx +72 -23
- package/client/components/common/input-context-menu.jsx +13 -5
- package/client/components/main/main.jsx +3 -0
- package/client/components/rdp/rdp-session.jsx +4 -8
- package/client/components/rdp/rdp.styl +15 -0
- package/client/components/setting-panel/bookmark-tree-list.jsx +1 -0
- package/client/components/setting-panel/deep-link-control.jsx +1 -1
- package/client/components/setting-panel/list.styl +10 -4
- package/client/components/setting-panel/setting-terminal.jsx +3 -2
- package/client/components/sftp/list-table-ui.jsx +29 -2
- package/client/components/sftp/paged-list.jsx +3 -8
- package/client/components/sidebar/history-item.jsx +13 -1
- package/client/components/sidebar/index.jsx +13 -10
- package/client/components/spice/spice.styl +7 -0
- package/client/components/tabs/add-btn-menu.jsx +2 -0
- package/client/components/tabs/no-session.jsx +25 -9
- package/client/components/tabs/no-session.styl +21 -0
- package/client/components/tabs/quick-connect.jsx +127 -0
- package/client/components/tabs/tabs.styl +1 -19
- package/client/components/terminal/highlight-addon.js +11 -0
- package/client/components/terminal/terminal.jsx +16 -1
- package/client/components/terminal/trzsz-client.js +6 -0
- package/client/components/terminal/xterm-loader.js +11 -0
- package/client/components/terminal-info/run-cmd.jsx +2 -1
- package/client/store/load-data.js +5 -1
- package/client/store/sync.js +1 -0
- package/client/store/tab.js +9 -0
- package/package.json +1 -1
|
@@ -8,12 +8,13 @@ import {
|
|
|
8
8
|
UpCircleOutlined,
|
|
9
9
|
BarsOutlined,
|
|
10
10
|
AppstoreOutlined,
|
|
11
|
-
|
|
11
|
+
ThunderboltOutlined
|
|
12
12
|
} from '@ant-design/icons'
|
|
13
|
-
import { Tooltip } from 'antd'
|
|
13
|
+
import { Tooltip, Popover } from 'antd'
|
|
14
14
|
import SideBarPanel from './sidebar-panel'
|
|
15
15
|
import TransferList from './transfer-list'
|
|
16
16
|
import MenuBtn from '../sys-menu/menu-btn'
|
|
17
|
+
import QuickConnect from '../tabs/quick-connect'
|
|
17
18
|
import {
|
|
18
19
|
sidebarWidth,
|
|
19
20
|
settingMap,
|
|
@@ -87,7 +88,6 @@ export default function Sidebar (props) {
|
|
|
87
88
|
|
|
88
89
|
const {
|
|
89
90
|
onNewSsh,
|
|
90
|
-
onNewSshAI,
|
|
91
91
|
openSetting,
|
|
92
92
|
openAbout,
|
|
93
93
|
openSettingSync,
|
|
@@ -144,14 +144,17 @@ export default function Sidebar (props) {
|
|
|
144
144
|
onClick={onNewSsh}
|
|
145
145
|
/>
|
|
146
146
|
</SideIcon>
|
|
147
|
-
<
|
|
148
|
-
|
|
147
|
+
<Popover
|
|
148
|
+
content={<QuickConnect inputOnly />}
|
|
149
|
+
trigger='click'
|
|
150
|
+
placement='right'
|
|
149
151
|
>
|
|
150
|
-
<
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
<div className='control-icon-wrap' title={e('quickConnect')}>
|
|
153
|
+
<ThunderboltOutlined
|
|
154
|
+
className='font20 iblock control-icon'
|
|
155
|
+
/>
|
|
156
|
+
</div>
|
|
157
|
+
</Popover>
|
|
155
158
|
<SideIcon
|
|
156
159
|
title={e(settingMap.bookmarks)}
|
|
157
160
|
active={bookmarksActive}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from '@ant-design/icons'
|
|
11
11
|
import BookmarksList from '../sidebar/bookmark-select'
|
|
12
12
|
import DragHandle from '../common/drag-handle'
|
|
13
|
+
import QuickConnect from './quick-connect'
|
|
13
14
|
|
|
14
15
|
const e = window.translate
|
|
15
16
|
|
|
@@ -87,6 +88,7 @@ export default function AddBtnMenu ({
|
|
|
87
88
|
>
|
|
88
89
|
<RobotOutlined /> {e('createBookmarkByAI')}
|
|
89
90
|
</div>
|
|
91
|
+
<QuickConnect batch={batch} inputOnly />
|
|
90
92
|
</div>
|
|
91
93
|
<div className='add-menu-list'>
|
|
92
94
|
<BookmarksList
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { Button } from 'antd'
|
|
2
|
+
import { RobotOutlined } from '@ant-design/icons'
|
|
2
3
|
import LogoElem from '../common/logo-elem.jsx'
|
|
3
4
|
import HistoryPanel from '../sidebar/history'
|
|
5
|
+
import QuickConnect from './quick-connect'
|
|
6
|
+
import './no-session.styl'
|
|
4
7
|
|
|
5
8
|
const e = window.translate
|
|
6
9
|
|
|
@@ -13,11 +16,15 @@ export default function NoSessionPanel ({ height, onNewTab, onNewSsh, batch }) {
|
|
|
13
16
|
const handleClick = () => {
|
|
14
17
|
window.openTabBatch = batch
|
|
15
18
|
}
|
|
19
|
+
|
|
20
|
+
const handleCreateAIBookmark = () => {
|
|
21
|
+
window.store.onNewSshAI()
|
|
22
|
+
}
|
|
23
|
+
|
|
16
24
|
const newTabDom = window.store.hasNodePty
|
|
17
25
|
? (
|
|
18
26
|
<Button
|
|
19
27
|
onClick={onNewTab}
|
|
20
|
-
size='large'
|
|
21
28
|
className='mg1r mg1b add-new-tab-btn'
|
|
22
29
|
>
|
|
23
30
|
{e('newTab')}
|
|
@@ -26,14 +33,23 @@ export default function NoSessionPanel ({ height, onNewTab, onNewSsh, batch }) {
|
|
|
26
33
|
: null
|
|
27
34
|
return (
|
|
28
35
|
<div className='no-sessions electerm-logo-bg' {...props}>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
<div className='pd1b'>
|
|
37
|
+
{newTabDom}
|
|
38
|
+
<Button
|
|
39
|
+
onClick={onNewSsh}
|
|
40
|
+
className='mg1r mg1b'
|
|
41
|
+
>
|
|
42
|
+
{e('newBookmark')}
|
|
43
|
+
</Button>
|
|
44
|
+
<Button
|
|
45
|
+
onClick={handleCreateAIBookmark}
|
|
46
|
+
className='mg1r mg1b'
|
|
47
|
+
icon={<RobotOutlined />}
|
|
48
|
+
>
|
|
49
|
+
{e('createBookmarkByAI')}
|
|
50
|
+
</Button>
|
|
51
|
+
<QuickConnect batch={batch} />
|
|
52
|
+
</div>
|
|
37
53
|
<div className='pd3'>
|
|
38
54
|
<LogoElem />
|
|
39
55
|
</div>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.no-sessions
|
|
2
|
+
background var(--main)
|
|
3
|
+
text-align center
|
|
4
|
+
padding 50px 0
|
|
5
|
+
position absolute
|
|
6
|
+
top 36px
|
|
7
|
+
left 0
|
|
8
|
+
right 0
|
|
9
|
+
bottom 0
|
|
10
|
+
z-index 11
|
|
11
|
+
.no-session-history
|
|
12
|
+
position absolute
|
|
13
|
+
top 320px
|
|
14
|
+
bottom 80px
|
|
15
|
+
left 0
|
|
16
|
+
right 0
|
|
17
|
+
.sidebar-panel-history
|
|
18
|
+
overflow-y auto
|
|
19
|
+
.pd2x
|
|
20
|
+
width 400px
|
|
21
|
+
margin 0 auto
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { useState, useRef, useEffect } from 'react'
|
|
2
|
+
import { Button, Space } from 'antd'
|
|
3
|
+
import { ArrowRightOutlined, ThunderboltOutlined } from '@ant-design/icons'
|
|
4
|
+
import message from '../common/message'
|
|
5
|
+
import InputAutoFocus from '../common/input-auto-focus'
|
|
6
|
+
import HelpIcon from '../common/help-icon'
|
|
7
|
+
|
|
8
|
+
const e = window.translate
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Connect using parsed options
|
|
12
|
+
* @param {object} opts - Connection options
|
|
13
|
+
* @param {number} batch - Batch number
|
|
14
|
+
*/
|
|
15
|
+
function connectWithOptions (opts, batch) {
|
|
16
|
+
const { store } = window
|
|
17
|
+
const tabOptions = {
|
|
18
|
+
...opts,
|
|
19
|
+
from: 'quickConnect',
|
|
20
|
+
batch
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
delete window.openTabBatch
|
|
24
|
+
store.addTab(tabOptions)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default function QuickConnect ({ batch, inputOnly }) {
|
|
28
|
+
const [showInput, setShowInput] = useState(false)
|
|
29
|
+
const [inputValue, setInputValue] = useState('')
|
|
30
|
+
const inputRef = useRef(null)
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (showInput && inputRef.current) {
|
|
34
|
+
inputRef.current.focus()
|
|
35
|
+
}
|
|
36
|
+
}, [showInput])
|
|
37
|
+
|
|
38
|
+
// When inputOnly is true, always show the input
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (inputOnly) {
|
|
41
|
+
setShowInput(true)
|
|
42
|
+
}
|
|
43
|
+
}, [inputOnly])
|
|
44
|
+
|
|
45
|
+
const handleToggle = () => {
|
|
46
|
+
setShowInput(!showInput)
|
|
47
|
+
if (showInput) {
|
|
48
|
+
setInputValue('')
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function handleChange (e) {
|
|
53
|
+
setInputValue(e.target.value)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const handleConnect = () => {
|
|
57
|
+
if (!inputValue.trim()) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const opts = window.store.parseQuickConnect(inputValue)
|
|
62
|
+
if (!opts) {
|
|
63
|
+
return message.error('Format error, please check the input', 10)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
connectWithOptions(opts, batch)
|
|
67
|
+
setInputValue('')
|
|
68
|
+
setShowInput(false)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function renderInput () {
|
|
72
|
+
if (!showInput && !inputOnly) {
|
|
73
|
+
return null
|
|
74
|
+
}
|
|
75
|
+
const inputProps = {
|
|
76
|
+
ref: inputRef,
|
|
77
|
+
value: inputValue,
|
|
78
|
+
onChange: handleChange,
|
|
79
|
+
className: 'width-100 quick-connect-input',
|
|
80
|
+
onPressEnter: handleConnect,
|
|
81
|
+
placeholder: 'ssh|rdp|vnc|spice|serial|http|https://[username]:[password]@host:port?opts={...}',
|
|
82
|
+
prefix: inputOnly ? <HelpIcon link={wiki} /> : undefined
|
|
83
|
+
}
|
|
84
|
+
const iconProps = {
|
|
85
|
+
onClick: handleConnect,
|
|
86
|
+
title: e('connect'),
|
|
87
|
+
icon: <ArrowRightOutlined />
|
|
88
|
+
}
|
|
89
|
+
const iconsProps1 = {
|
|
90
|
+
icon: <ThunderboltOutlined />
|
|
91
|
+
}
|
|
92
|
+
return (
|
|
93
|
+
<Space.Compact className='pd1y pd2x width-100'>
|
|
94
|
+
<Button
|
|
95
|
+
{...iconsProps1}
|
|
96
|
+
/>
|
|
97
|
+
<InputAutoFocus {...inputProps} />
|
|
98
|
+
<Button
|
|
99
|
+
{...iconProps}
|
|
100
|
+
/>
|
|
101
|
+
</Space.Compact>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
const wiki = 'https://github.com/electerm/electerm/wiki/quick-connect'
|
|
105
|
+
|
|
106
|
+
// If inputOnly is true, don't show the button, just render input directly
|
|
107
|
+
if (inputOnly) {
|
|
108
|
+
return renderInput()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const btnProps = {
|
|
112
|
+
onClick: handleToggle,
|
|
113
|
+
icon: <ThunderboltOutlined />,
|
|
114
|
+
title: e('quickConnect')
|
|
115
|
+
}
|
|
116
|
+
return (
|
|
117
|
+
<>
|
|
118
|
+
<Button
|
|
119
|
+
{...btnProps}
|
|
120
|
+
>
|
|
121
|
+
<span className='mg1r'>{e('quickConnect')}</span>
|
|
122
|
+
<HelpIcon link={wiki} />
|
|
123
|
+
</Button>
|
|
124
|
+
{renderInput()}
|
|
125
|
+
</>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
@@ -218,30 +218,12 @@
|
|
|
218
218
|
.tab-title-tag
|
|
219
219
|
margin-right 5px
|
|
220
220
|
font-size 14px
|
|
221
|
-
|
|
222
|
-
background var(--main)
|
|
223
|
-
text-align center
|
|
224
|
-
padding 50px 0
|
|
225
|
-
position absolute
|
|
226
|
-
top 36px
|
|
227
|
-
left 0
|
|
228
|
-
right 0
|
|
229
|
-
bottom 0
|
|
230
|
-
z-index 11
|
|
221
|
+
|
|
231
222
|
.tab-count
|
|
232
223
|
border-radius 10px 2px 2px 10px
|
|
233
224
|
padding 0 4px
|
|
234
225
|
height 20px
|
|
235
226
|
line-height 21px
|
|
236
|
-
.no-session-history
|
|
237
|
-
position absolute
|
|
238
|
-
top 280px
|
|
239
|
-
bottom 80px
|
|
240
|
-
left 0
|
|
241
|
-
right 0
|
|
242
|
-
.sidebar-panel-history .pd2x
|
|
243
|
-
width 400px
|
|
244
|
-
margin 0 auto
|
|
245
227
|
|
|
246
228
|
.ssh-tunnel-list-wrapper
|
|
247
229
|
max-height 300px
|
|
@@ -36,6 +36,11 @@ export class KeywordHighlighterAddon {
|
|
|
36
36
|
return colorMap[color] || colorMap.red
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
containsDcsSequence = (text) => {
|
|
40
|
+
const ESC = String.fromCharCode(27)
|
|
41
|
+
return text.includes(ESC + 'P')
|
|
42
|
+
}
|
|
43
|
+
|
|
39
44
|
escape = str => {
|
|
40
45
|
return str.replace(/\\x1B/g, '\\x1B')
|
|
41
46
|
.replace(/\033/g, '\\033')
|
|
@@ -96,6 +101,12 @@ export class KeywordHighlighterAddon {
|
|
|
96
101
|
this.originalWrite = terminal.write.bind(terminal)
|
|
97
102
|
const self = this
|
|
98
103
|
terminal.write = function (data) {
|
|
104
|
+
if (typeof data !== 'string') {
|
|
105
|
+
return self.originalWrite(data)
|
|
106
|
+
}
|
|
107
|
+
if (self.containsDcsSequence(data)) {
|
|
108
|
+
return self.originalWrite(data)
|
|
109
|
+
}
|
|
99
110
|
self.originalWrite(
|
|
100
111
|
terminal.displayRaw ? self.escape(data) : self.highlightKeywords(data)
|
|
101
112
|
)
|
|
@@ -57,7 +57,8 @@ import {
|
|
|
57
57
|
loadWebglAddon,
|
|
58
58
|
loadSearchAddon,
|
|
59
59
|
loadLigaturesAddon,
|
|
60
|
-
loadUnicode11Addon
|
|
60
|
+
loadUnicode11Addon,
|
|
61
|
+
loadImageAddon
|
|
61
62
|
} from './xterm-loader.js'
|
|
62
63
|
|
|
63
64
|
const e = window.translate
|
|
@@ -164,6 +165,7 @@ class Term extends Component {
|
|
|
164
165
|
this.searchAddon = null
|
|
165
166
|
this.fitAddon = null
|
|
166
167
|
this.cmdAddon = null
|
|
168
|
+
this.imageAddon = null
|
|
167
169
|
// Clear the notification if it exists
|
|
168
170
|
if (this.socketCloseWarning) {
|
|
169
171
|
notification.destroy(this.socketCloseWarning.key)
|
|
@@ -816,6 +818,19 @@ class Term extends Component {
|
|
|
816
818
|
this.searchAddon.onDidChangeResults(this.onSearchResultsChange)
|
|
817
819
|
const Unicode11Addon = await loadUnicode11Addon()
|
|
818
820
|
const unicode11Addon = new Unicode11Addon()
|
|
821
|
+
if (config.enableSixel !== false) {
|
|
822
|
+
try {
|
|
823
|
+
const ImageAddon = await loadImageAddon()
|
|
824
|
+
this.imageAddon = new ImageAddon({
|
|
825
|
+
enableSizeReports: false,
|
|
826
|
+
sixelSupport: true,
|
|
827
|
+
iipSupport: false
|
|
828
|
+
})
|
|
829
|
+
term.loadAddon(this.imageAddon)
|
|
830
|
+
} catch (err) {
|
|
831
|
+
console.error('load sixel addon failed', err)
|
|
832
|
+
}
|
|
833
|
+
}
|
|
819
834
|
term.loadAddon(unicode11Addon)
|
|
820
835
|
term.loadAddon(ligtureAddon)
|
|
821
836
|
term.unicode.activeVersion = '11'
|
|
@@ -44,6 +44,12 @@ export class TrzszClient extends TransferClientBase {
|
|
|
44
44
|
handleServerEvent (msg) {
|
|
45
45
|
const { event } = msg
|
|
46
46
|
|
|
47
|
+
// Ignore events that arrive after the session has already ended
|
|
48
|
+
// (e.g. async error from cancelled transfer)
|
|
49
|
+
if (!this.isActive && event !== 'receive-start' && event !== 'send-start') {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
47
53
|
switch (event) {
|
|
48
54
|
case 'receive-start':
|
|
49
55
|
this.onReceiveStart()
|
|
@@ -72,6 +72,13 @@ export async function loadUnicode11Addon () {
|
|
|
72
72
|
return window.xtermAddons.Unicode11Addon
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
export async function loadImageAddon () {
|
|
76
|
+
if (window.xtermAddons.ImageAddon) return window.xtermAddons.ImageAddon
|
|
77
|
+
const mod = await import('@xterm/addon-image')
|
|
78
|
+
window.xtermAddons.ImageAddon = mod.ImageAddon
|
|
79
|
+
return window.xtermAddons.ImageAddon
|
|
80
|
+
}
|
|
81
|
+
|
|
75
82
|
export function getTerminal () {
|
|
76
83
|
return window.xtermAddons.Terminal
|
|
77
84
|
}
|
|
@@ -107,3 +114,7 @@ export function getLigaturesAddon () {
|
|
|
107
114
|
export function getUnicode11Addon () {
|
|
108
115
|
return window.xtermAddons.Unicode11Addon
|
|
109
116
|
}
|
|
117
|
+
|
|
118
|
+
export function getImageAddon () {
|
|
119
|
+
return window.xtermAddons.ImageAddon
|
|
120
|
+
}
|
|
@@ -167,6 +167,7 @@ function InfoGetter (props) {
|
|
|
167
167
|
formatter,
|
|
168
168
|
delay
|
|
169
169
|
} = props.options
|
|
170
|
+
const { pid } = props
|
|
170
171
|
const cms = cmds || [cmd]
|
|
171
172
|
useEffect(() => {
|
|
172
173
|
const run = async () => {
|
|
@@ -180,7 +181,7 @@ function InfoGetter (props) {
|
|
|
180
181
|
return () => {
|
|
181
182
|
clearInterval(ref)
|
|
182
183
|
}
|
|
183
|
-
}, [])
|
|
184
|
+
}, [pid])
|
|
184
185
|
return null
|
|
185
186
|
}
|
|
186
187
|
|
|
@@ -13,6 +13,7 @@ import { initWsCommon } from '../common/fetch-from-server'
|
|
|
13
13
|
import safeParse from '../common/parse-json-safe'
|
|
14
14
|
import initWatch from './watch'
|
|
15
15
|
import { refsStatic } from '../components/common/ref'
|
|
16
|
+
import { parseQuickConnect } from '../common/parse-quick-connect'
|
|
16
17
|
|
|
17
18
|
function getHost (argv, opts) {
|
|
18
19
|
const arr = argv
|
|
@@ -219,7 +220,10 @@ export default (Store) => {
|
|
|
219
220
|
Store.prototype.checkPendingDeepLink = async function () {
|
|
220
221
|
const pending = await window.pre.runGlobalAsync('getPendingDeepLink')
|
|
221
222
|
if (pending) {
|
|
222
|
-
|
|
223
|
+
window.store.addTab(pending)
|
|
223
224
|
}
|
|
224
225
|
}
|
|
226
|
+
Store.prototype.parseQuickConnect = function (url) {
|
|
227
|
+
return parseQuickConnect(url)
|
|
228
|
+
}
|
|
225
229
|
}
|
package/client/store/sync.js
CHANGED
package/client/store/tab.js
CHANGED
|
@@ -341,6 +341,15 @@ export default Store => {
|
|
|
341
341
|
const { tabs } = store
|
|
342
342
|
newTab.tabCount = store.nextTabCount()
|
|
343
343
|
newTab.batch = batch ?? newTab.batch ?? window.openTabBatch ?? window.store.currentLayoutBatch
|
|
344
|
+
if (!newTab.id) {
|
|
345
|
+
newTab.id = generate()
|
|
346
|
+
}
|
|
347
|
+
if (!newTab.status) {
|
|
348
|
+
newTab.status = statusMap.processing
|
|
349
|
+
}
|
|
350
|
+
if (!newTab.pane) {
|
|
351
|
+
newTab.pane = paneMap.terminal
|
|
352
|
+
}
|
|
344
353
|
if (typeof index === 'number' && index >= 0 && index <= tabs.length) {
|
|
345
354
|
tabs.splice(index, 0, newTab)
|
|
346
355
|
} else {
|