@electerm/electerm-react 2.3.151 → 2.3.166
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 +4 -2
- package/client/common/db.js +2 -1
- package/client/common/init-setting-item.js +7 -0
- package/client/components/common/modal.jsx +89 -0
- package/client/components/common/modal.styl +77 -0
- package/client/components/common/notification-with-details.jsx +34 -0
- package/client/components/file-transfer/conflict-resolve.jsx +2 -1
- package/client/components/file-transfer/transfer-speed-format.js +6 -0
- package/client/components/file-transfer/transfer.jsx +5 -2
- package/client/components/file-transfer/transports-action-store.jsx +14 -1
- package/client/components/main/main.jsx +2 -0
- package/client/components/setting-panel/setting-common.jsx +4 -3
- package/client/components/setting-panel/start-session-select.jsx +146 -21
- package/client/components/setting-panel/text-bg-modal.jsx +15 -4
- package/client/components/sftp/file-info-modal.jsx +2 -1
- package/client/components/sftp/file-item.jsx +2 -0
- package/client/components/sidebar/info-modal.jsx +53 -34
- package/client/components/sidebar/info.styl +0 -7
- package/client/components/tabs/index.jsx +6 -58
- package/client/components/tabs/layout-menu.jsx +75 -0
- package/client/components/tabs/layout-select.jsx +60 -0
- package/client/components/tabs/tabs.styl +64 -0
- package/client/components/tabs/workspace-save-modal.jsx +117 -0
- package/client/components/tabs/workspace-select.jsx +79 -0
- package/client/components/terminal/attach-addon-custom.js +7 -1
- package/client/components/terminal/terminal-interactive.jsx +2 -1
- package/client/components/terminal/terminal.jsx +0 -1
- package/client/components/text-editor/text-editor.jsx +2 -1
- package/client/components/tree-list/move-item-modal.jsx +2 -1
- package/client/components/vnc/vnc-session.jsx +2 -2
- package/client/components/widgets/widget-control.jsx +4 -5
- package/client/components/widgets/widget-form.jsx +3 -8
- package/client/components/widgets/widget-instance.jsx +44 -9
- package/client/components/widgets/widget-notification-with-details.jsx +34 -0
- package/client/css/basic.styl +3 -1
- package/client/store/init-state.js +4 -0
- package/client/store/load-data.js +15 -6
- package/client/store/store.js +2 -0
- package/client/store/workspace.js +108 -0
- package/package.json +1 -1
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
SearchOutlined
|
|
6
6
|
} from '@ant-design/icons'
|
|
7
7
|
import buildGroupData from '../bookmark-form/common/bookmark-group-tree-format'
|
|
8
|
-
import { Tree,
|
|
8
|
+
import { Tree, Button, Input } from 'antd'
|
|
9
|
+
import Modal from '../common/modal'
|
|
9
10
|
import { auto } from 'manate/react'
|
|
10
11
|
const e = window.translate
|
|
11
12
|
|
|
@@ -10,9 +10,9 @@ import {
|
|
|
10
10
|
import {
|
|
11
11
|
Spin,
|
|
12
12
|
message,
|
|
13
|
-
Modal,
|
|
14
13
|
Tag
|
|
15
14
|
} from 'antd'
|
|
15
|
+
import Modal from '../common/modal'
|
|
16
16
|
import * as ls from '../../common/safe-local-storage'
|
|
17
17
|
import { copy } from '../../common/clipboard'
|
|
18
18
|
import resolutions from '../rdp/resolutions'
|
|
@@ -250,7 +250,7 @@ export default class VncSession extends RdpSession {
|
|
|
250
250
|
title: e('credentialsRequired'),
|
|
251
251
|
content: this.renderForm(['password']),
|
|
252
252
|
footer: null,
|
|
253
|
-
|
|
253
|
+
open: true
|
|
254
254
|
}
|
|
255
255
|
return (
|
|
256
256
|
<Modal
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import React, { useState } from 'react'
|
|
5
5
|
import WidgetForm from './widget-form'
|
|
6
|
-
import {
|
|
7
|
-
message
|
|
8
|
-
} from 'antd'
|
|
6
|
+
import { showMsg } from './widget-notification-with-details'
|
|
9
7
|
|
|
10
8
|
export default function WidgetControl ({ formData }) {
|
|
11
9
|
const [loading, setLoading] = useState(false)
|
|
@@ -30,9 +28,9 @@ export default function WidgetControl ({ formData }) {
|
|
|
30
28
|
} = result
|
|
31
29
|
if (!instanceId) {
|
|
32
30
|
if (success === false) {
|
|
33
|
-
|
|
31
|
+
showMsg('Failed to run widget', 'error', null, 10, error || '')
|
|
34
32
|
} else {
|
|
35
|
-
|
|
33
|
+
showMsg(msg, 'success', null, 10)
|
|
36
34
|
}
|
|
37
35
|
return
|
|
38
36
|
}
|
|
@@ -45,6 +43,7 @@ export default function WidgetControl ({ formData }) {
|
|
|
45
43
|
config
|
|
46
44
|
}
|
|
47
45
|
window.store.widgetInstances.push(instance)
|
|
46
|
+
showMsg(msg, 'success', result.serverInfo, 10)
|
|
48
47
|
} catch (err) {
|
|
49
48
|
console.error('Failed to run widget:', err)
|
|
50
49
|
} finally {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Widget form component
|
|
3
3
|
*/
|
|
4
4
|
import React from 'react'
|
|
5
|
-
import { Form, Input, InputNumber, Switch, Select, Button
|
|
5
|
+
import { Form, Input, InputNumber, Switch, Select, Button } from 'antd'
|
|
6
6
|
import { formItemLayout, tailFormItemLayout } from '../../common/form-layout'
|
|
7
7
|
|
|
8
8
|
export default function WidgetForm ({ widget, onSubmit, loading }) {
|
|
@@ -18,12 +18,7 @@ export default function WidgetForm ({ widget, onSubmit, loading }) {
|
|
|
18
18
|
const txt = isInstanceWidget ? 'Start widget' : 'Run widget'
|
|
19
19
|
|
|
20
20
|
const handleSubmit = async (values) => {
|
|
21
|
-
|
|
22
|
-
await onSubmit(values)
|
|
23
|
-
message.success('Widget started successfully')
|
|
24
|
-
} catch (error) {
|
|
25
|
-
message.error('Failed to start widget: ' + error.message)
|
|
26
|
-
}
|
|
21
|
+
onSubmit(values)
|
|
27
22
|
}
|
|
28
23
|
|
|
29
24
|
const renderFormItem = (config) => {
|
|
@@ -86,7 +81,7 @@ export default function WidgetForm ({ widget, onSubmit, loading }) {
|
|
|
86
81
|
|
|
87
82
|
return (
|
|
88
83
|
<div className='widget-form'>
|
|
89
|
-
<div className='pd1b'>
|
|
84
|
+
<div className='pd1b alignright'>
|
|
90
85
|
<h4>{info.name}</h4>
|
|
91
86
|
<p>{info.description}</p>
|
|
92
87
|
</div>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { Popconfirm } from 'antd'
|
|
2
|
-
import { CloseOutlined } from '@ant-design/icons'
|
|
1
|
+
import { Popconfirm, Popover } from 'antd'
|
|
2
|
+
import { CloseOutlined, CopyOutlined } from '@ant-design/icons'
|
|
3
|
+
import { copy } from '../../common/clipboard'
|
|
3
4
|
|
|
4
5
|
const e = window.translate
|
|
5
6
|
|
|
6
7
|
export default function WidgetInstance ({ item }) {
|
|
7
|
-
const { id, title } = item
|
|
8
|
+
const { id, title, serverInfo } = item
|
|
8
9
|
const cls = 'item-list-unit'
|
|
9
10
|
const delProps = {
|
|
10
11
|
title: e('del'),
|
|
@@ -25,17 +26,51 @@ export default function WidgetInstance ({ item }) {
|
|
|
25
26
|
cancelText: e('cancel'),
|
|
26
27
|
placement: 'top'
|
|
27
28
|
}
|
|
29
|
+
const handleCopy = () => {
|
|
30
|
+
if (serverInfo && serverInfo.url) {
|
|
31
|
+
copy(serverInfo.url)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const popoverContent = serverInfo
|
|
35
|
+
? (
|
|
36
|
+
<div>
|
|
37
|
+
<div style={{ display: 'flex', alignItems: 'center' }}>
|
|
38
|
+
<span>URL: {serverInfo.url}</span>
|
|
39
|
+
<CopyOutlined
|
|
40
|
+
className='pointer mg1l'
|
|
41
|
+
onClick={handleCopy}
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
44
|
+
<div>Path: {serverInfo.path}</div>
|
|
45
|
+
</div>
|
|
46
|
+
)
|
|
47
|
+
: null
|
|
48
|
+
const titleDiv = (
|
|
49
|
+
<div
|
|
50
|
+
title={title}
|
|
51
|
+
className='elli pd1y pd2x list-item-title'
|
|
52
|
+
>
|
|
53
|
+
{title}
|
|
54
|
+
</div>
|
|
55
|
+
)
|
|
28
56
|
return (
|
|
29
57
|
<div
|
|
30
58
|
key={id}
|
|
31
59
|
className={cls}
|
|
32
60
|
>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
61
|
+
{
|
|
62
|
+
serverInfo
|
|
63
|
+
? (
|
|
64
|
+
<Popover
|
|
65
|
+
content={popoverContent}
|
|
66
|
+
trigger='hover'
|
|
67
|
+
placement='top'
|
|
68
|
+
>
|
|
69
|
+
{titleDiv}
|
|
70
|
+
</Popover>
|
|
71
|
+
)
|
|
72
|
+
: titleDiv
|
|
73
|
+
}
|
|
39
74
|
<Popconfirm
|
|
40
75
|
{...popProps}
|
|
41
76
|
>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { notification } from 'antd'
|
|
2
|
+
import { CopyOutlined } from '@ant-design/icons'
|
|
3
|
+
import { copy } from '../../common/clipboard'
|
|
4
|
+
|
|
5
|
+
export function showMsg (message, type = 'success', serverInfo = null, duration = 10, description = '') {
|
|
6
|
+
const handleCopy = () => {
|
|
7
|
+
if (serverInfo && serverInfo.url) {
|
|
8
|
+
copy(serverInfo.url)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let desc = description
|
|
13
|
+
if (serverInfo) {
|
|
14
|
+
desc = (
|
|
15
|
+
<div>
|
|
16
|
+
{description && <div>{description}</div>}
|
|
17
|
+
<div style={{ display: 'flex', alignItems: 'center' }}>
|
|
18
|
+
<span>URL: {serverInfo.url}</span>
|
|
19
|
+
<CopyOutlined
|
|
20
|
+
className='pointer mg1l'
|
|
21
|
+
onClick={handleCopy}
|
|
22
|
+
/>
|
|
23
|
+
</div>
|
|
24
|
+
<div>Path: {serverInfo.path}</div>
|
|
25
|
+
</div>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
notification[type]({
|
|
30
|
+
message,
|
|
31
|
+
description: desc,
|
|
32
|
+
duration
|
|
33
|
+
})
|
|
34
|
+
}
|
package/client/css/basic.styl
CHANGED
|
@@ -75,6 +75,10 @@ export default () => {
|
|
|
75
75
|
resolutions: ls.getItemJSON(resolutionsLsKey, []),
|
|
76
76
|
terminalCommandHistory: new Set(ls.getItemJSON(cmdHistoryKey, [])),
|
|
77
77
|
|
|
78
|
+
// workspaces
|
|
79
|
+
workspaces: [],
|
|
80
|
+
workspaceSaveModalVisible: false,
|
|
81
|
+
|
|
78
82
|
// init session control
|
|
79
83
|
selectedSessions: [],
|
|
80
84
|
sessionModalVisible: false,
|
|
@@ -115,13 +115,22 @@ export async function addTabFromCommandLine (store, opts) {
|
|
|
115
115
|
export default (Store) => {
|
|
116
116
|
Store.prototype.openInitSessions = function () {
|
|
117
117
|
const { store } = window
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
118
|
+
const onStartSessions = store.config.onStartSessions
|
|
119
|
+
|
|
120
|
+
// If onStartSessions is a string, it's a workspace ID
|
|
121
|
+
if (typeof onStartSessions === 'string' && onStartSessions) {
|
|
122
|
+
store.loadWorkspace(onStartSessions)
|
|
123
|
+
} else {
|
|
124
|
+
// Otherwise, it's an array of bookmark IDs
|
|
125
|
+
const arr = Array.isArray(onStartSessions) ? onStartSessions : []
|
|
126
|
+
for (const s of arr) {
|
|
127
|
+
store.onSelectBookmark(s)
|
|
128
|
+
}
|
|
129
|
+
if (!arr.length && store.config.initDefaultTabOnStart) {
|
|
130
|
+
store.initFirstTab()
|
|
131
|
+
}
|
|
124
132
|
}
|
|
133
|
+
|
|
125
134
|
store.confirmLoad()
|
|
126
135
|
const { initTime, loadTime } = window.pre.runSync('getLoadTime')
|
|
127
136
|
if (loadTime) {
|
package/client/store/store.js
CHANGED
|
@@ -25,6 +25,7 @@ import batchInputHistory from './batch-input-history'
|
|
|
25
25
|
import transferExtend from './transfer-list'
|
|
26
26
|
import addressBookmarkExtend from './address-bookmark'
|
|
27
27
|
import widgetsExtend from './widgets'
|
|
28
|
+
import workspaceExtend from './workspace'
|
|
28
29
|
import isColorDark from '../common/is-color-dark'
|
|
29
30
|
import { getReverseColor } from '../common/reverse-color'
|
|
30
31
|
import { uniq } from 'lodash-es'
|
|
@@ -297,5 +298,6 @@ batchInputHistory(Store)
|
|
|
297
298
|
transferExtend(Store)
|
|
298
299
|
addressBookmarkExtend(Store)
|
|
299
300
|
widgetsExtend(Store)
|
|
301
|
+
workspaceExtend(Store)
|
|
300
302
|
|
|
301
303
|
export const StateStore = Store
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workspace related functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
settingMap
|
|
7
|
+
} from '../common/constants'
|
|
8
|
+
import getInitItem from '../common/init-setting-item'
|
|
9
|
+
import generate from '../common/uid'
|
|
10
|
+
|
|
11
|
+
export default Store => {
|
|
12
|
+
/**
|
|
13
|
+
* Get current workspace state (layout + tabs for each batch)
|
|
14
|
+
*/
|
|
15
|
+
Store.prototype.getCurrentWorkspaceState = function () {
|
|
16
|
+
const { store } = window
|
|
17
|
+
const { layout, tabs } = store
|
|
18
|
+
// Group tabs by batch and get bookmark srcIds
|
|
19
|
+
const tabsByBatch = {}
|
|
20
|
+
for (const tab of tabs) {
|
|
21
|
+
const batch = tab.batch || 0
|
|
22
|
+
if (!tabsByBatch[batch]) {
|
|
23
|
+
tabsByBatch[batch] = []
|
|
24
|
+
}
|
|
25
|
+
// Store srcId (bookmark id) if available, otherwise store basic connection info
|
|
26
|
+
if (tab.srcId) {
|
|
27
|
+
tabsByBatch[batch].push({
|
|
28
|
+
srcId: tab.srcId
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
layout,
|
|
34
|
+
tabsByBatch
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Save current workspace
|
|
40
|
+
*/
|
|
41
|
+
Store.prototype.saveWorkspace = function (name, id = null) {
|
|
42
|
+
const { store } = window
|
|
43
|
+
const state = store.getCurrentWorkspaceState()
|
|
44
|
+
const workspace = {
|
|
45
|
+
id: id || generate(),
|
|
46
|
+
name,
|
|
47
|
+
...state,
|
|
48
|
+
createdAt: Date.now(),
|
|
49
|
+
updatedAt: Date.now()
|
|
50
|
+
}
|
|
51
|
+
if (id) {
|
|
52
|
+
// Update existing
|
|
53
|
+
store.editItem(id, workspace, settingMap.workspaces)
|
|
54
|
+
} else {
|
|
55
|
+
// Add new
|
|
56
|
+
store.addItem(workspace, settingMap.workspaces)
|
|
57
|
+
}
|
|
58
|
+
return workspace
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Load a workspace - set layout and open tabs
|
|
63
|
+
*/
|
|
64
|
+
Store.prototype.loadWorkspace = function (workspaceId) {
|
|
65
|
+
const { store } = window
|
|
66
|
+
const workspace = store.workspaces.find(w => w.id === workspaceId)
|
|
67
|
+
if (!workspace) {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
const { layout, tabsByBatch } = workspace
|
|
71
|
+
|
|
72
|
+
// Close all existing tabs first
|
|
73
|
+
store.removeTabs(() => true)
|
|
74
|
+
|
|
75
|
+
// Set layout
|
|
76
|
+
store.setLayout(layout)
|
|
77
|
+
// Open tabs for each batch
|
|
78
|
+
for (const [batchStr, tabInfos] of Object.entries(tabsByBatch)) {
|
|
79
|
+
const batch = parseInt(batchStr, 10)
|
|
80
|
+
for (const tabInfo of tabInfos) {
|
|
81
|
+
if (tabInfo.srcId) {
|
|
82
|
+
// Open from bookmark
|
|
83
|
+
window.openTabBatch = batch
|
|
84
|
+
store.onSelectBookmark(tabInfo.srcId)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Delete a workspace
|
|
92
|
+
*/
|
|
93
|
+
Store.prototype.deleteWorkspace = function (id) {
|
|
94
|
+
window.store.delItem({ id }, settingMap.workspaces)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Open workspace settings
|
|
99
|
+
*/
|
|
100
|
+
Store.prototype.openWorkspaceSettings = function () {
|
|
101
|
+
const { store } = window
|
|
102
|
+
store.storeAssign({
|
|
103
|
+
settingTab: settingMap.workspaces
|
|
104
|
+
})
|
|
105
|
+
store.setSettingItem(getInitItem([], settingMap.workspaces))
|
|
106
|
+
store.openSettingModal()
|
|
107
|
+
}
|
|
108
|
+
}
|