@electerm/electerm-react 1.39.5 → 1.39.31
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 +7 -3
- package/client/common/create-title.jsx +12 -2
- package/client/common/default-setting.js +5 -0
- package/client/common/init-setting-item.js +6 -0
- package/client/components/bookmark-form/form-ssh-common.jsx +1 -1
- package/client/components/bookmark-form/index.jsx +6 -2
- package/client/components/bookmark-form/rdp-form-ui.jsx +1 -1
- package/client/components/bookmark-form/rdp-form.jsx +1 -1
- package/client/components/bookmark-form/render-auth-ssh.jsx +27 -1
- package/client/components/bookmark-form/render-ssh-tunnel.jsx +60 -31
- package/client/components/bookmark-form/use-ui.jsx +12 -0
- package/client/components/bookmark-form/vnc-form-ui.jsx +179 -0
- package/client/components/bookmark-form/vnc-form.jsx +16 -0
- package/client/components/footer/footer-entry.jsx +13 -6
- package/client/components/main/term-fullscreen.styl +4 -1
- package/client/components/profile/profile-form-elem.jsx +87 -0
- package/client/components/profile/profile-form.jsx +33 -0
- package/client/components/profile/profile-list.jsx +79 -0
- package/client/components/profile/profile-transport-mod.jsx +5 -0
- package/client/components/profile/profile-transport.jsx +12 -0
- package/client/components/quick-commands/quick-command-transport-mod.jsx +13 -14
- package/client/components/quick-commands/quick-commands-list-form.jsx +54 -3
- package/client/components/rdp/rdp-session.jsx +53 -36
- package/client/components/session/session.jsx +14 -3
- package/client/components/session/session.styl +7 -2
- package/client/components/setting-panel/setting-modal.jsx +23 -6
- package/client/components/setting-panel/setting-terminal.jsx +1 -1
- package/client/components/setting-panel/setting-wrap.jsx +5 -1
- package/client/components/setting-panel/setting-wrap.styl +5 -3
- package/client/components/setting-panel/tab-profiles.jsx +38 -0
- package/client/components/sftp/list-table-ui.jsx +99 -46
- package/client/components/sftp/sftp-entry.jsx +1 -0
- package/client/components/sftp/sftp.styl +4 -1
- package/client/components/sftp/transfer-common.js +1 -1
- package/client/components/tabs/index.jsx +1 -1
- package/client/components/tabs/tab.jsx +9 -4
- package/client/components/terminal/index.jsx +5 -5
- package/client/components/vnc/vnc-form.jsx +66 -0
- package/client/components/vnc/vnc-session.jsx +297 -0
- package/client/store/common.js +21 -0
- package/client/store/index.js +1 -0
- package/client/store/init-state.js +4 -23
- package/client/store/load-data.js +9 -2
- package/client/store/sync.js +7 -3
- package/package.json +1 -1
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import RdpSession from '../rdp/rdp-session'
|
|
2
|
+
import { createTerm } from '../terminal/terminal-apis'
|
|
3
|
+
import deepCopy from 'json-deep-copy'
|
|
4
|
+
import clone from '../../common/to-simple-obj'
|
|
5
|
+
import { handleErr } from '../../common/fetch'
|
|
6
|
+
import {
|
|
7
|
+
statusMap
|
|
8
|
+
} from '../../common/constants'
|
|
9
|
+
import {
|
|
10
|
+
Spin,
|
|
11
|
+
message,
|
|
12
|
+
Modal,
|
|
13
|
+
Tag
|
|
14
|
+
} from 'antd'
|
|
15
|
+
import * as ls from '../../common/safe-local-storage'
|
|
16
|
+
import { copy } from '../../common/clipboard'
|
|
17
|
+
import resolutions from '../rdp/resolutions'
|
|
18
|
+
import RFB from '@novnc/novnc/core/rfb'
|
|
19
|
+
import VncForm from './vnc-form'
|
|
20
|
+
|
|
21
|
+
const { prefix } = window
|
|
22
|
+
const e = prefix('form')
|
|
23
|
+
|
|
24
|
+
export default class VncSession extends RdpSession {
|
|
25
|
+
constructor (props) {
|
|
26
|
+
const id = `vnc-reso-${props.tab.host}`
|
|
27
|
+
const resObj = ls.getItemJSON(id, resolutions[0])
|
|
28
|
+
super(props)
|
|
29
|
+
this.state = {
|
|
30
|
+
types: [],
|
|
31
|
+
showConfirm: false,
|
|
32
|
+
loading: false,
|
|
33
|
+
aspectRatio: 4 / 3,
|
|
34
|
+
name: '',
|
|
35
|
+
...resObj
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
componentDidMount () {
|
|
40
|
+
this.remoteInit()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
componentWillUnmount () {
|
|
44
|
+
this.rfb && this.rfb.disconnect()
|
|
45
|
+
delete this.rfb
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// computeProps = () => {
|
|
49
|
+
// const {
|
|
50
|
+
// height,
|
|
51
|
+
// width,
|
|
52
|
+
// tabsHeight,
|
|
53
|
+
// leftSidebarWidth,
|
|
54
|
+
// pinned,
|
|
55
|
+
// openedSideBar
|
|
56
|
+
// } = this.props
|
|
57
|
+
// return {
|
|
58
|
+
// width: width - (pinned && openedSideBar ? leftSidebarWidth : 0),
|
|
59
|
+
// height: height - tabsHeight
|
|
60
|
+
// }
|
|
61
|
+
// }
|
|
62
|
+
|
|
63
|
+
remoteInit = async (term = this.term) => {
|
|
64
|
+
this.setState({
|
|
65
|
+
loading: true
|
|
66
|
+
})
|
|
67
|
+
const { config } = this.props
|
|
68
|
+
const {
|
|
69
|
+
host,
|
|
70
|
+
port,
|
|
71
|
+
tokenElecterm,
|
|
72
|
+
server = ''
|
|
73
|
+
} = config
|
|
74
|
+
const { sessionId, id } = this.props
|
|
75
|
+
const tab = deepCopy(this.props.tab || {})
|
|
76
|
+
const {
|
|
77
|
+
type,
|
|
78
|
+
term: terminalType,
|
|
79
|
+
viewOnly = false,
|
|
80
|
+
scaleViewport = true,
|
|
81
|
+
username,
|
|
82
|
+
password
|
|
83
|
+
} = tab
|
|
84
|
+
const opts = clone({
|
|
85
|
+
term: terminalType || config.terminalType,
|
|
86
|
+
sessionId,
|
|
87
|
+
tabId: id,
|
|
88
|
+
srcTabId: tab.id,
|
|
89
|
+
termType: type,
|
|
90
|
+
...tab
|
|
91
|
+
})
|
|
92
|
+
let pid = await createTerm(opts)
|
|
93
|
+
.catch(err => {
|
|
94
|
+
const text = err.message
|
|
95
|
+
handleErr({ message: text })
|
|
96
|
+
})
|
|
97
|
+
pid = pid || ''
|
|
98
|
+
this.setState({
|
|
99
|
+
loading: false
|
|
100
|
+
})
|
|
101
|
+
if (!pid) {
|
|
102
|
+
this.setStatus(statusMap.error)
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
this.setStatus(statusMap.success)
|
|
106
|
+
this.pid = pid
|
|
107
|
+
const hs = server
|
|
108
|
+
? server.replace(/https?:\/\//, '')
|
|
109
|
+
: `${host}:${port}`
|
|
110
|
+
const pre = server.startsWith('https') ? 'wss' : 'ws'
|
|
111
|
+
const { width, height } = this.state
|
|
112
|
+
const wsUrl = `${pre}://${hs}/vnc/${pid}?sessionId=${sessionId}&token=${tokenElecterm}&width=${width}&height=${height}`
|
|
113
|
+
const vncOpts = {
|
|
114
|
+
scaleViewport,
|
|
115
|
+
viewOnly,
|
|
116
|
+
style: {
|
|
117
|
+
width: width + 'px',
|
|
118
|
+
height: height + 'px',
|
|
119
|
+
overflow: 'scroll'
|
|
120
|
+
},
|
|
121
|
+
credentials: {}
|
|
122
|
+
}
|
|
123
|
+
if (username) {
|
|
124
|
+
vncOpts.credentials.username = username
|
|
125
|
+
}
|
|
126
|
+
if (password) {
|
|
127
|
+
vncOpts.credentials.password = password
|
|
128
|
+
}
|
|
129
|
+
const rfb = new RFB(
|
|
130
|
+
this.getDom(),
|
|
131
|
+
wsUrl,
|
|
132
|
+
vncOpts
|
|
133
|
+
)
|
|
134
|
+
const events = [
|
|
135
|
+
'connect',
|
|
136
|
+
'disconnect',
|
|
137
|
+
'credentialsrequired',
|
|
138
|
+
'securityfailure',
|
|
139
|
+
'clipboard',
|
|
140
|
+
'bell',
|
|
141
|
+
'desktopname',
|
|
142
|
+
'capabilities'
|
|
143
|
+
]
|
|
144
|
+
for (const event of events) {
|
|
145
|
+
rfb.addEventListener(event, this[`on${window.capitalizeFirstLetter(event)}`])
|
|
146
|
+
}
|
|
147
|
+
this.rfb = rfb
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
onConnect = (event) => {
|
|
151
|
+
// console.log('onConnect', event)
|
|
152
|
+
this.setStatus(statusMap.success)
|
|
153
|
+
this.setState({
|
|
154
|
+
loading: false
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
onDisconnect = () => {
|
|
159
|
+
this.setStatus(statusMap.error)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
onSecurityfailure = (event) => {
|
|
163
|
+
// console.log('onSecurityFailure', event)
|
|
164
|
+
message.error('Security Failure: ' + event.detail?.reason)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
onOk = (res) => {
|
|
168
|
+
this.setState({
|
|
169
|
+
showConfirm: false
|
|
170
|
+
})
|
|
171
|
+
this.rfb?.sendCredentials(res)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
onCredentialsrequired = (event) => {
|
|
175
|
+
this.setState({
|
|
176
|
+
types: event.detail?.types || [],
|
|
177
|
+
showConfirm: true
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
renderForm (types = this.state.types) {
|
|
182
|
+
return (
|
|
183
|
+
<VncForm
|
|
184
|
+
types={types}
|
|
185
|
+
handleFinish={this.onOk}
|
|
186
|
+
/>
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
onClipboard = (event) => {
|
|
191
|
+
// console.log('onClipboard', event)
|
|
192
|
+
copy(event.detail.text)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
onBell = (event) => {
|
|
196
|
+
// console.log('Bell', event)
|
|
197
|
+
message.warning('Bell')
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
onDesktopname = (event) => {
|
|
201
|
+
this.setState({
|
|
202
|
+
name: event?.detail?.name || ''
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
onCapabilities = (capabilities) => {
|
|
207
|
+
console.log('onCapabilities', capabilities)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
getDom = () => {
|
|
211
|
+
const id = 'canvas_' + this.props.tab.id
|
|
212
|
+
return document.getElementById(id)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
handleReInit = () => {
|
|
216
|
+
this.rfb?.disconnect()
|
|
217
|
+
delete this.rfb
|
|
218
|
+
this.remoteInit()
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
renderInfo () {
|
|
222
|
+
const {
|
|
223
|
+
name
|
|
224
|
+
} = this.state
|
|
225
|
+
const {
|
|
226
|
+
host,
|
|
227
|
+
port,
|
|
228
|
+
username
|
|
229
|
+
} = this.props.tab
|
|
230
|
+
return (
|
|
231
|
+
<span className='mg2l mg2r'>
|
|
232
|
+
<b>{name}</b> {username}@{host}:{port}
|
|
233
|
+
</span>
|
|
234
|
+
)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
renderHelp = () => {
|
|
238
|
+
return (
|
|
239
|
+
<Tag color='red' className='mg1l'>Beta</Tag>
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
renderConfirm () {
|
|
244
|
+
const {
|
|
245
|
+
showConfirm
|
|
246
|
+
} = this.state
|
|
247
|
+
if (!showConfirm) {
|
|
248
|
+
return null
|
|
249
|
+
}
|
|
250
|
+
const confirmProps = {
|
|
251
|
+
title: e('credentialsRequired'),
|
|
252
|
+
content: this.renderForm(['password']),
|
|
253
|
+
footer: null,
|
|
254
|
+
visible: true
|
|
255
|
+
}
|
|
256
|
+
return (
|
|
257
|
+
<Modal
|
|
258
|
+
{...confirmProps}
|
|
259
|
+
>
|
|
260
|
+
{this.renderForm()}
|
|
261
|
+
</Modal>
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
render () {
|
|
266
|
+
const { width: w, height: h } = this.computeProps()
|
|
267
|
+
const vncProps = {
|
|
268
|
+
style: {
|
|
269
|
+
width: w + 'px',
|
|
270
|
+
height: h + 'px'
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
const { width, height, loading } = this.state
|
|
274
|
+
const divProps = {
|
|
275
|
+
style: {
|
|
276
|
+
width: width + 'px',
|
|
277
|
+
height: height + 'px'
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return (
|
|
281
|
+
<Spin spinning={loading}>
|
|
282
|
+
<div
|
|
283
|
+
{...vncProps}
|
|
284
|
+
className='rdp-session-wrap pd1'
|
|
285
|
+
>
|
|
286
|
+
{this.renderControl()}
|
|
287
|
+
<div
|
|
288
|
+
{...divProps}
|
|
289
|
+
className='vnc-session-wrap session-v-wrap'
|
|
290
|
+
id={'canvas_' + this.props.tab.id}
|
|
291
|
+
/>
|
|
292
|
+
{this.renderConfirm()}
|
|
293
|
+
</div>
|
|
294
|
+
</Spin>
|
|
295
|
+
)
|
|
296
|
+
}
|
|
297
|
+
}
|
package/client/store/common.js
CHANGED
|
@@ -211,4 +211,25 @@ export default Store => {
|
|
|
211
211
|
terminalInfos: arr
|
|
212
212
|
})
|
|
213
213
|
}
|
|
214
|
+
|
|
215
|
+
Store.prototype.applyProfile = function (tab) {
|
|
216
|
+
const {
|
|
217
|
+
authType,
|
|
218
|
+
profile
|
|
219
|
+
} = tab
|
|
220
|
+
if (authType !== 'profiles') {
|
|
221
|
+
return tab
|
|
222
|
+
}
|
|
223
|
+
const p = window.store.profiles.find(x => x.id === profile)
|
|
224
|
+
if (!p) {
|
|
225
|
+
return tab
|
|
226
|
+
}
|
|
227
|
+
// delete tab.password
|
|
228
|
+
// delete tab.privateKey
|
|
229
|
+
// delete tab.passphrase
|
|
230
|
+
return {
|
|
231
|
+
...tab,
|
|
232
|
+
...p
|
|
233
|
+
}
|
|
234
|
+
}
|
|
214
235
|
}
|
package/client/store/index.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
import {
|
|
6
6
|
settingMap,
|
|
7
7
|
defaultBookmarkGroupId,
|
|
8
|
-
newBookmarkIdPrefix,
|
|
9
8
|
fileOperationsMap,
|
|
10
9
|
syncTypes,
|
|
11
10
|
infoTabs,
|
|
@@ -22,14 +21,12 @@ import {
|
|
|
22
21
|
qmSortByFrequencyKey,
|
|
23
22
|
resolutionsLsKey
|
|
24
23
|
} from '../common/constants'
|
|
25
|
-
import { buildDefaultThemes
|
|
24
|
+
import { buildDefaultThemes } from '../common/terminal-theme'
|
|
26
25
|
import * as ls from '../common/safe-local-storage'
|
|
26
|
+
import initSettingItem from '../common/init-setting-item'
|
|
27
27
|
|
|
28
28
|
const { prefix } = window
|
|
29
29
|
const t = prefix('terminalThemes')
|
|
30
|
-
const e = prefix('common')
|
|
31
|
-
const newQuickCommand = 'newQuickCommand'
|
|
32
|
-
const q = prefix('quickCommands')
|
|
33
30
|
|
|
34
31
|
function getDefaultBookmarkGroups (bookmarks) {
|
|
35
32
|
return [
|
|
@@ -41,23 +38,6 @@ function getDefaultBookmarkGroups (bookmarks) {
|
|
|
41
38
|
]
|
|
42
39
|
}
|
|
43
40
|
|
|
44
|
-
export const getInitItem = (arr, tab) => {
|
|
45
|
-
if (tab === settingMap.history) {
|
|
46
|
-
return arr[0] || {}
|
|
47
|
-
} else if (tab === settingMap.bookmarks) {
|
|
48
|
-
return { id: newBookmarkIdPrefix + ':' + (Date.now()), title: '' }
|
|
49
|
-
} else if (tab === settingMap.setting) {
|
|
50
|
-
return { id: '', title: e('common') }
|
|
51
|
-
} else if (tab === settingMap.terminalThemes) {
|
|
52
|
-
return buildNewTheme()
|
|
53
|
-
} else if (tab === settingMap.quickCommands) {
|
|
54
|
-
return {
|
|
55
|
-
id: '',
|
|
56
|
-
name: q(newQuickCommand)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
41
|
export default () => {
|
|
62
42
|
return {
|
|
63
43
|
// common
|
|
@@ -70,6 +50,7 @@ export default () => {
|
|
|
70
50
|
termFocused: false,
|
|
71
51
|
_history: '[]',
|
|
72
52
|
_bookmarks: '[]',
|
|
53
|
+
_profiles: '[]',
|
|
73
54
|
_bookmarkGroups: JSON.stringify(
|
|
74
55
|
getDefaultBookmarkGroups([])
|
|
75
56
|
),
|
|
@@ -113,7 +94,7 @@ export default () => {
|
|
|
113
94
|
|
|
114
95
|
// for settings related
|
|
115
96
|
_setting: '',
|
|
116
|
-
_settingItem: JSON.stringify(
|
|
97
|
+
_settingItem: JSON.stringify(initSettingItem([], settingMap.bookmarks)),
|
|
117
98
|
settingTab: settingMap.bookmarks, // setting tab
|
|
118
99
|
autofocustrigger: Date.now(),
|
|
119
100
|
bookmarkId: undefined,
|
|
@@ -60,7 +60,8 @@ export async function addTabFromCommandLine (store, opts) {
|
|
|
60
60
|
enableSsh: !options.sftpOnly,
|
|
61
61
|
authType: 'password',
|
|
62
62
|
pane: options.type || 'terminal',
|
|
63
|
-
term: defaultSettings.terminalType
|
|
63
|
+
term: defaultSettings.terminalType,
|
|
64
|
+
startDirectoryLocal: options.initFolder
|
|
64
65
|
}
|
|
65
66
|
if (options.setEnv) {
|
|
66
67
|
update.setEnv = options.setEnv
|
|
@@ -81,6 +82,12 @@ export async function addTabFromCommandLine (store, opts) {
|
|
|
81
82
|
log.debug('command line opts', conf)
|
|
82
83
|
if (conf.username && conf.host) {
|
|
83
84
|
store.addTab(conf)
|
|
85
|
+
} else if (
|
|
86
|
+
options.initFolder &&
|
|
87
|
+
!(store.config.onStartSessions || []).length &&
|
|
88
|
+
store.config.initDefaultTabOnStart
|
|
89
|
+
) {
|
|
90
|
+
window.initFolder = options.initFolder
|
|
84
91
|
}
|
|
85
92
|
if (options && options.batchOp) {
|
|
86
93
|
window.store.runBatchOp(options.batchOp)
|
|
@@ -170,7 +177,7 @@ export default (Store) => {
|
|
|
170
177
|
await Promise.all(all)
|
|
171
178
|
.then(arr => {
|
|
172
179
|
for (const { name, data } of arr) {
|
|
173
|
-
ext['_' + name] = data
|
|
180
|
+
ext['_' + name] = data || '[]'
|
|
174
181
|
}
|
|
175
182
|
})
|
|
176
183
|
ext.lastDataUpdateTime = await getData('lastDataUpdateTime') || 0
|
package/client/store/sync.js
CHANGED
|
@@ -233,9 +233,13 @@ export default (Store) => {
|
|
|
233
233
|
for (const n of names) {
|
|
234
234
|
let str = get(gist, `files["${n}.json"].content`)
|
|
235
235
|
if (!str) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
236
|
+
if (n === settingMap.bookmarks) {
|
|
237
|
+
store.isSyncingSetting = false
|
|
238
|
+
store.isSyncDownload = false
|
|
239
|
+
throw new Error(('Seems you have a empty gist, you can try use existing gist ID or upload first'))
|
|
240
|
+
} else {
|
|
241
|
+
continue
|
|
242
|
+
}
|
|
239
243
|
}
|
|
240
244
|
if (!isJSON(str)) {
|
|
241
245
|
str = await window.pre.runGlobalAsync('decryptAsync', str, pass)
|