@electerm/electerm-react 2.3.136 → 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/batch-op/batch-op.jsx +3 -8
- package/client/components/bookmark-form/common/color-picker.jsx +16 -5
- package/client/components/bookmark-form/common/color-picker.styl +1 -2
- package/client/components/bookmark-form/common/connection-hopping.jsx +1 -0
- package/client/components/bookmark-form/config/common-fields.js +1 -0
- package/client/components/common/drawer.jsx +62 -0
- package/client/components/common/drawer.styl +34 -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/profile/profile-form.jsx +1 -1
- package/client/components/quick-commands/qm.styl +2 -1
- package/client/components/quick-commands/quick-commands-form.jsx +1 -1
- package/client/components/setting-panel/list.jsx +1 -1
- package/client/components/setting-panel/setting-common.jsx +5 -4
- package/client/components/setting-panel/setting-terminal.jsx +1 -1
- package/client/components/setting-panel/setting-wrap.jsx +4 -10
- package/client/components/setting-panel/setting-wrap.styl +8 -6
- 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/sftp/paged-list.jsx +2 -1
- package/client/components/sftp/sftp-entry.jsx +1 -1
- package/client/components/sftp/sftp.styl +13 -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 +115 -30
- package/client/components/tree-list/tree-list.jsx +1 -1
- package/client/components/tree-list/tree-list.styl +6 -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
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout select content component
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react'
|
|
6
|
+
import {
|
|
7
|
+
SingleIcon,
|
|
8
|
+
TwoColumnsIcon,
|
|
9
|
+
ThreeColumnsIcon,
|
|
10
|
+
TwoRowsIcon,
|
|
11
|
+
ThreeRowsIcon,
|
|
12
|
+
Grid2x2Icon,
|
|
13
|
+
TwoRowsRightIcon,
|
|
14
|
+
TwoColumnsBottomIcon
|
|
15
|
+
} from '../icons/split-icons'
|
|
16
|
+
import { splitMapDesc } from '../../common/constants'
|
|
17
|
+
|
|
18
|
+
const e = window.translate
|
|
19
|
+
|
|
20
|
+
const iconMaps = {
|
|
21
|
+
single: SingleIcon,
|
|
22
|
+
twoColumns: TwoColumnsIcon,
|
|
23
|
+
threeColumns: ThreeColumnsIcon,
|
|
24
|
+
twoRows: TwoRowsIcon,
|
|
25
|
+
threeRows: ThreeRowsIcon,
|
|
26
|
+
grid2x2: Grid2x2Icon,
|
|
27
|
+
twoRowsRight: TwoRowsRightIcon,
|
|
28
|
+
twoColumnsBottom: TwoColumnsBottomIcon
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getLayoutIcon (layout) {
|
|
32
|
+
return iconMaps[layout]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default function LayoutSelect (props) {
|
|
36
|
+
const { layout } = props
|
|
37
|
+
|
|
38
|
+
function handleChangeLayout (key) {
|
|
39
|
+
window.store.setLayout(key)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div className='layout-menu-content'>
|
|
44
|
+
{Object.keys(splitMapDesc).map((t) => {
|
|
45
|
+
const v = splitMapDesc[t]
|
|
46
|
+
const Icon = getLayoutIcon(v)
|
|
47
|
+
const isActive = layout === t
|
|
48
|
+
return (
|
|
49
|
+
<div
|
|
50
|
+
key={t}
|
|
51
|
+
className={`layout-menu-item ${isActive ? 'active' : ''}`}
|
|
52
|
+
onClick={() => handleChangeLayout(t)}
|
|
53
|
+
>
|
|
54
|
+
<Icon /> {e(v)}
|
|
55
|
+
</div>
|
|
56
|
+
)
|
|
57
|
+
})}
|
|
58
|
+
</div>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
@@ -244,3 +244,67 @@
|
|
|
244
244
|
max-height 300px
|
|
245
245
|
overflow-y auto
|
|
246
246
|
|
|
247
|
+
// Layout and Workspace dropdown styles
|
|
248
|
+
.layout-workspace-dropdown
|
|
249
|
+
background var(--main)
|
|
250
|
+
border-radius 4px
|
|
251
|
+
box-shadow 0 2px 8px rgba(0, 0, 0, 0.15)
|
|
252
|
+
min-width 200px
|
|
253
|
+
padding 8px
|
|
254
|
+
.ant-tabs-nav
|
|
255
|
+
margin-bottom 8px
|
|
256
|
+
|
|
257
|
+
.layout-menu-content
|
|
258
|
+
max-height 300px
|
|
259
|
+
overflow-y auto
|
|
260
|
+
|
|
261
|
+
.layout-menu-item
|
|
262
|
+
padding 6px 12px
|
|
263
|
+
cursor pointer
|
|
264
|
+
border-radius 4px
|
|
265
|
+
display flex
|
|
266
|
+
align-items center
|
|
267
|
+
gap 8px
|
|
268
|
+
color var(--text)
|
|
269
|
+
&:hover
|
|
270
|
+
background var(--main-dark)
|
|
271
|
+
&.active
|
|
272
|
+
background var(--primary)
|
|
273
|
+
color #fff
|
|
274
|
+
|
|
275
|
+
.workspace-menu-content
|
|
276
|
+
max-height 300px
|
|
277
|
+
overflow-y auto
|
|
278
|
+
|
|
279
|
+
.workspace-save-btn
|
|
280
|
+
margin-bottom 8px
|
|
281
|
+
|
|
282
|
+
.workspace-list
|
|
283
|
+
display flex
|
|
284
|
+
flex-direction column
|
|
285
|
+
gap 4px
|
|
286
|
+
|
|
287
|
+
.workspace-item
|
|
288
|
+
padding 6px 12px
|
|
289
|
+
cursor pointer
|
|
290
|
+
border-radius 4px
|
|
291
|
+
display flex
|
|
292
|
+
align-items center
|
|
293
|
+
justify-content space-between
|
|
294
|
+
color var(--text)
|
|
295
|
+
&:hover
|
|
296
|
+
background var(--main-dark)
|
|
297
|
+
.workspace-delete-icon
|
|
298
|
+
opacity 0
|
|
299
|
+
color var(--text-dark)
|
|
300
|
+
&:hover
|
|
301
|
+
color var(--error)
|
|
302
|
+
&:hover .workspace-delete-icon
|
|
303
|
+
opacity 1
|
|
304
|
+
|
|
305
|
+
.workspace-name
|
|
306
|
+
flex 1
|
|
307
|
+
overflow hidden
|
|
308
|
+
text-overflow ellipsis
|
|
309
|
+
white-space nowrap
|
|
310
|
+
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace save modal component - standalone modal
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React, { useState } from 'react'
|
|
6
|
+
import { auto } from 'manate/react'
|
|
7
|
+
import Modal from '../common/modal'
|
|
8
|
+
import { Input, Select, Button, Space, message, Radio } from 'antd'
|
|
9
|
+
import { SaveOutlined, EditOutlined } from '@ant-design/icons'
|
|
10
|
+
|
|
11
|
+
const e = window.translate
|
|
12
|
+
|
|
13
|
+
export default auto(function WorkspaceSaveModal ({ store }) {
|
|
14
|
+
const { workspaceSaveModalVisible, workspaces } = store
|
|
15
|
+
const [name, setName] = useState('')
|
|
16
|
+
const [selectedId, setSelectedId] = useState(null)
|
|
17
|
+
const [saveMode, setSaveMode] = useState('new') // 'new' or 'overwrite'
|
|
18
|
+
|
|
19
|
+
if (!workspaceSaveModalVisible) {
|
|
20
|
+
return null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function handleClose () {
|
|
24
|
+
window.store.workspaceSaveModalVisible = false
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function handleSave () {
|
|
28
|
+
if (saveMode === 'new') {
|
|
29
|
+
if (!name.trim()) {
|
|
30
|
+
message.error(e('name needed'))
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
window.store.saveWorkspace(name.trim())
|
|
34
|
+
message.success(e('saved'))
|
|
35
|
+
} else {
|
|
36
|
+
if (!selectedId) {
|
|
37
|
+
message.error('please Select Workspace')
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
const ws = workspaces.find(w => w.id === selectedId)
|
|
41
|
+
window.store.saveWorkspace(ws?.name || name, selectedId)
|
|
42
|
+
message.success(e('saved'))
|
|
43
|
+
}
|
|
44
|
+
setName('')
|
|
45
|
+
setSelectedId(null)
|
|
46
|
+
setSaveMode('new')
|
|
47
|
+
handleClose()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function handleCancel () {
|
|
51
|
+
setName('')
|
|
52
|
+
setSelectedId(null)
|
|
53
|
+
setSaveMode('new')
|
|
54
|
+
handleClose()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const options = workspaces.map(w => ({
|
|
58
|
+
label: w.name,
|
|
59
|
+
value: w.id
|
|
60
|
+
}))
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<Modal
|
|
64
|
+
title={e('save')}
|
|
65
|
+
open={workspaceSaveModalVisible}
|
|
66
|
+
onCancel={handleCancel}
|
|
67
|
+
footer={null}
|
|
68
|
+
width={400}
|
|
69
|
+
>
|
|
70
|
+
<div className='pd1y'>
|
|
71
|
+
<Space direction='vertical' block>
|
|
72
|
+
<Radio.Group
|
|
73
|
+
value={saveMode}
|
|
74
|
+
onChange={ev => setSaveMode(ev.target.value)}
|
|
75
|
+
>
|
|
76
|
+
<Radio value='new'>
|
|
77
|
+
<SaveOutlined className='mg1r' />
|
|
78
|
+
{e('saveAsNew')}
|
|
79
|
+
</Radio>
|
|
80
|
+
<Radio value='overwrite' disabled={!workspaces.length}>
|
|
81
|
+
<EditOutlined className='mg1r' />
|
|
82
|
+
{e('overwrite')}
|
|
83
|
+
</Radio>
|
|
84
|
+
</Radio.Group>
|
|
85
|
+
|
|
86
|
+
{saveMode === 'new'
|
|
87
|
+
? (
|
|
88
|
+
<Input
|
|
89
|
+
placeholder={e('name')}
|
|
90
|
+
value={name}
|
|
91
|
+
onChange={e => setName(e.target.value)}
|
|
92
|
+
onPressEnter={handleSave}
|
|
93
|
+
/>
|
|
94
|
+
)
|
|
95
|
+
: (
|
|
96
|
+
<Select
|
|
97
|
+
placeholder={e('workspaces')}
|
|
98
|
+
value={selectedId}
|
|
99
|
+
onChange={setSelectedId}
|
|
100
|
+
options={options}
|
|
101
|
+
style={{ width: '100%' }}
|
|
102
|
+
/>
|
|
103
|
+
)}
|
|
104
|
+
|
|
105
|
+
<div className='pd1t'>
|
|
106
|
+
<Button type='primary' onClick={handleSave}>
|
|
107
|
+
{e('save')}
|
|
108
|
+
</Button>
|
|
109
|
+
<Button className='mg1l' onClick={handleCancel}>
|
|
110
|
+
{e('cancel')}
|
|
111
|
+
</Button>
|
|
112
|
+
</div>
|
|
113
|
+
</Space>
|
|
114
|
+
</div>
|
|
115
|
+
</Modal>
|
|
116
|
+
)
|
|
117
|
+
})
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace select content component
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react'
|
|
6
|
+
import { Button, Empty, Popconfirm } from 'antd'
|
|
7
|
+
import {
|
|
8
|
+
SaveOutlined,
|
|
9
|
+
DeleteOutlined
|
|
10
|
+
} from '@ant-design/icons'
|
|
11
|
+
import { auto } from 'manate/react'
|
|
12
|
+
|
|
13
|
+
const e = window.translate
|
|
14
|
+
|
|
15
|
+
export default auto(function WorkspaceSelect (props) {
|
|
16
|
+
const { store } = props
|
|
17
|
+
const { workspaces } = store
|
|
18
|
+
|
|
19
|
+
function handleLoadWorkspace (id) {
|
|
20
|
+
window.store.loadWorkspace(id)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function handleDeleteWorkspace (id, ev) {
|
|
24
|
+
ev.stopPropagation()
|
|
25
|
+
window.store.deleteWorkspace(id)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function handleSaveClick () {
|
|
29
|
+
window.store.workspaceSaveModalVisible = true
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div className='workspace-menu-content'>
|
|
34
|
+
<div className='workspace-save-btn pd1b'>
|
|
35
|
+
<Button
|
|
36
|
+
type='primary'
|
|
37
|
+
icon={<SaveOutlined />}
|
|
38
|
+
size='small'
|
|
39
|
+
onClick={handleSaveClick}
|
|
40
|
+
block
|
|
41
|
+
>
|
|
42
|
+
{e('save')}
|
|
43
|
+
</Button>
|
|
44
|
+
</div>
|
|
45
|
+
{workspaces.length === 0
|
|
46
|
+
? (
|
|
47
|
+
<Empty
|
|
48
|
+
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
|
49
|
+
description='No items'
|
|
50
|
+
/>
|
|
51
|
+
)
|
|
52
|
+
: (
|
|
53
|
+
<div className='workspace-list'>
|
|
54
|
+
{workspaces.map(ws => (
|
|
55
|
+
<div
|
|
56
|
+
key={ws.id}
|
|
57
|
+
className='workspace-item'
|
|
58
|
+
onClick={() => handleLoadWorkspace(ws.id)}
|
|
59
|
+
>
|
|
60
|
+
<span className='workspace-name'>{ws.name}</span>
|
|
61
|
+
<Popconfirm
|
|
62
|
+
title={e('del') + '?'}
|
|
63
|
+
onConfirm={(ev) => handleDeleteWorkspace(ws.id, ev)}
|
|
64
|
+
onCancel={(ev) => ev.stopPropagation()}
|
|
65
|
+
okText={e('ok')}
|
|
66
|
+
cancelText={e('cancel')}
|
|
67
|
+
>
|
|
68
|
+
<DeleteOutlined
|
|
69
|
+
className='workspace-delete-icon'
|
|
70
|
+
onClick={(ev) => ev.stopPropagation()}
|
|
71
|
+
/>
|
|
72
|
+
</Popconfirm>
|
|
73
|
+
</div>
|
|
74
|
+
))}
|
|
75
|
+
</div>
|
|
76
|
+
)}
|
|
77
|
+
</div>
|
|
78
|
+
)
|
|
79
|
+
})
|
|
@@ -34,7 +34,13 @@ export default class AttachAddonCustom extends AttachAddon {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
onMsg = (ev) => {
|
|
37
|
-
|
|
37
|
+
// When in alternate screen mode (like vim, less, or TUI apps like Claude Code),
|
|
38
|
+
// bypass trzsz processing to avoid interference with the application's display
|
|
39
|
+
if (this.term?.buffer?.active?.type === 'alternate') {
|
|
40
|
+
this.writeToTerminal(ev.data)
|
|
41
|
+
} else {
|
|
42
|
+
this.trzsz.processServerOutput(ev.data)
|
|
43
|
+
}
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
writeToTerminal = (data) => {
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { useEffect, useState } from 'react'
|
|
6
|
-
import {
|
|
6
|
+
import { Form, Button } from 'antd'
|
|
7
|
+
import Modal from '../common/modal'
|
|
7
8
|
import InputAutoFocus from '../common/input-auto-focus'
|
|
8
9
|
import wait from '../../common/wait'
|
|
9
10
|
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import { PureComponent } from 'react'
|
|
6
6
|
import TextEditorForm from './text-editor-form'
|
|
7
|
-
import { Spin
|
|
7
|
+
import { Spin } from 'antd'
|
|
8
|
+
import Modal from '../common/modal'
|
|
8
9
|
import resolve from '../../common/resolve'
|
|
9
10
|
import { refsStatic, refs } from '../common/ref'
|
|
10
11
|
|
|
@@ -1,17 +1,63 @@
|
|
|
1
|
-
// render bookmark select, use antd tree
|
|
1
|
+
// render bookmark select, use antd tree
|
|
2
2
|
import { useState, useEffect } from 'react'
|
|
3
3
|
import {
|
|
4
|
-
MergeOutlined
|
|
4
|
+
MergeOutlined,
|
|
5
|
+
SearchOutlined
|
|
5
6
|
} from '@ant-design/icons'
|
|
6
7
|
import buildGroupData from '../bookmark-form/common/bookmark-group-tree-format'
|
|
7
|
-
import {
|
|
8
|
+
import { Tree, Button, Input } from 'antd'
|
|
9
|
+
import Modal from '../common/modal'
|
|
8
10
|
import { auto } from 'manate/react'
|
|
9
11
|
const e = window.translate
|
|
10
12
|
|
|
11
13
|
const rootId = '__root__'
|
|
12
14
|
|
|
15
|
+
// Helper function to filter tree data based on search text
|
|
16
|
+
function filterTreeData (data, searchText) {
|
|
17
|
+
if (!searchText) {
|
|
18
|
+
return data
|
|
19
|
+
}
|
|
20
|
+
const lowerSearch = searchText.toLowerCase()
|
|
21
|
+
|
|
22
|
+
function filterNodes (nodes) {
|
|
23
|
+
return nodes.reduce((acc, node) => {
|
|
24
|
+
const titleText = typeof node.title === 'string'
|
|
25
|
+
? node.title
|
|
26
|
+
: (node.title?.props?.children?.[1] || node.title?.props?.children || '')
|
|
27
|
+
const titleStr = String(titleText).toLowerCase()
|
|
28
|
+
const children = node.children ? filterNodes(node.children) : []
|
|
29
|
+
|
|
30
|
+
if (titleStr.includes(lowerSearch) || children.length > 0) {
|
|
31
|
+
acc.push({
|
|
32
|
+
...node,
|
|
33
|
+
children: children.length > 0 ? children : node.children
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
return acc
|
|
37
|
+
}, [])
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return filterNodes(data)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Helper function to get all keys from tree data
|
|
44
|
+
function getAllKeys (data) {
|
|
45
|
+
const keys = []
|
|
46
|
+
function traverse (nodes) {
|
|
47
|
+
for (const node of nodes) {
|
|
48
|
+
keys.push(node.key)
|
|
49
|
+
if (node.children) {
|
|
50
|
+
traverse(node.children)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
traverse(data)
|
|
55
|
+
return keys
|
|
56
|
+
}
|
|
57
|
+
|
|
13
58
|
export default auto(function MoveItemModal (props) {
|
|
14
59
|
const [groupId, setGroupId] = useState(undefined)
|
|
60
|
+
const [searchText, setSearchText] = useState('')
|
|
15
61
|
const {
|
|
16
62
|
openMoveModal,
|
|
17
63
|
moveItem,
|
|
@@ -27,10 +73,11 @@ export default auto(function MoveItemModal (props) {
|
|
|
27
73
|
})
|
|
28
74
|
}
|
|
29
75
|
|
|
30
|
-
// Reset groupId when modal opens
|
|
76
|
+
// Reset groupId and search when modal opens
|
|
31
77
|
useEffect(() => {
|
|
32
78
|
if (openMoveModal) {
|
|
33
79
|
setGroupId(undefined)
|
|
80
|
+
setSearchText('')
|
|
34
81
|
}
|
|
35
82
|
}, [openMoveModal])
|
|
36
83
|
|
|
@@ -60,6 +107,31 @@ export default auto(function MoveItemModal (props) {
|
|
|
60
107
|
disabled: false
|
|
61
108
|
})
|
|
62
109
|
}
|
|
110
|
+
|
|
111
|
+
// Filter tree data based on search
|
|
112
|
+
const filteredData = filterTreeData(data, searchText)
|
|
113
|
+
const expandedKeys = getAllKeys(filteredData)
|
|
114
|
+
|
|
115
|
+
function onTreeSelect (selectedKeys) {
|
|
116
|
+
if (selectedKeys.length > 0) {
|
|
117
|
+
// Find the node to check if it's disabled
|
|
118
|
+
const findNode = (nodes, key) => {
|
|
119
|
+
for (const node of nodes) {
|
|
120
|
+
if (node.key === key) return node
|
|
121
|
+
if (node.children) {
|
|
122
|
+
const found = findNode(node.children, key)
|
|
123
|
+
if (found) return found
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return null
|
|
127
|
+
}
|
|
128
|
+
const node = findNode(data, selectedKeys[0])
|
|
129
|
+
if (node && !node.disabled) {
|
|
130
|
+
setGroupId(selectedKeys[0])
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
63
135
|
function onSelect () {
|
|
64
136
|
const {
|
|
65
137
|
bookmarkGroups
|
|
@@ -109,43 +181,56 @@ export default auto(function MoveItemModal (props) {
|
|
|
109
181
|
}
|
|
110
182
|
onCancelMoveItem()
|
|
111
183
|
}
|
|
184
|
+
|
|
185
|
+
const footer = (
|
|
186
|
+
<>
|
|
187
|
+
<Button
|
|
188
|
+
type='primary'
|
|
189
|
+
onClick={onSelect}
|
|
190
|
+
disabled={!groupId}
|
|
191
|
+
>
|
|
192
|
+
{e('ok')}
|
|
193
|
+
</Button>
|
|
194
|
+
<Button
|
|
195
|
+
onClick={onCancelMoveItem}
|
|
196
|
+
className='mg1l'
|
|
197
|
+
>
|
|
198
|
+
{e('cancel')}
|
|
199
|
+
</Button>
|
|
200
|
+
</>
|
|
201
|
+
)
|
|
202
|
+
|
|
112
203
|
const modalProps = {
|
|
113
204
|
open: openMoveModal,
|
|
114
205
|
title: e('moveTo'),
|
|
115
|
-
footer
|
|
206
|
+
footer,
|
|
116
207
|
onCancel: onCancelMoveItem
|
|
117
208
|
}
|
|
209
|
+
|
|
118
210
|
const treeProps = {
|
|
119
|
-
treeData:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
treeDefaultExpandAll: true,
|
|
126
|
-
className: 'width-100'
|
|
211
|
+
treeData: filteredData,
|
|
212
|
+
onSelect: onTreeSelect,
|
|
213
|
+
selectedKeys: groupId ? [groupId] : [],
|
|
214
|
+
expandedKeys,
|
|
215
|
+
autoExpandParent: true,
|
|
216
|
+
className: 'width-100 move-item-tree'
|
|
127
217
|
}
|
|
218
|
+
|
|
128
219
|
return (
|
|
129
220
|
<Modal {...modalProps}>
|
|
130
|
-
<div className='
|
|
131
|
-
<
|
|
132
|
-
{
|
|
221
|
+
<div className='pd1b'>
|
|
222
|
+
<Input
|
|
223
|
+
placeholder={e('search')}
|
|
224
|
+
prefix={<SearchOutlined />}
|
|
225
|
+
value={searchText}
|
|
226
|
+
onChange={e => setSearchText(e.target.value)}
|
|
227
|
+
allowClear
|
|
133
228
|
/>
|
|
134
229
|
</div>
|
|
135
|
-
<div className='
|
|
136
|
-
<
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
disabled={!groupId}
|
|
140
|
-
>
|
|
141
|
-
{e('ok')}
|
|
142
|
-
</Button>
|
|
143
|
-
<Button
|
|
144
|
-
onClick={onCancelMoveItem}
|
|
145
|
-
className='mg1l'
|
|
146
|
-
>
|
|
147
|
-
{e('cancel')}
|
|
148
|
-
</Button>
|
|
230
|
+
<div className='move-item-tree-wrap'>
|
|
231
|
+
<Tree
|
|
232
|
+
{...treeProps}
|
|
233
|
+
/>
|
|
149
234
|
</div>
|
|
150
235
|
</Modal>
|
|
151
236
|
)
|
|
@@ -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>
|