@electerm/electerm-react 2.10.26 → 2.11.6
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/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/bookmark-form/ai-bookmark-form.jsx +40 -1
- 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/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 +130 -0
- package/client/components/tabs/tabs.styl +1 -19
- package/client/components/terminal/highlight-addon.js +11 -0
- package/client/components/terminal/terminal-interactive.jsx +1 -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 +4 -0
- package/client/store/sync.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bookmark from history modal - used to create bookmark from history item
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react'
|
|
6
|
+
import { Button, message } from 'antd'
|
|
7
|
+
import { PlusOutlined } from '@ant-design/icons'
|
|
8
|
+
import Modal from '../common/modal'
|
|
9
|
+
import { refsStatic } from '../common/ref'
|
|
10
|
+
import AICategorySelect from './common/ai-category-select.jsx'
|
|
11
|
+
import generate from '../../common/uid'
|
|
12
|
+
import copy from 'json-deep-copy'
|
|
13
|
+
|
|
14
|
+
const e = window.translate
|
|
15
|
+
|
|
16
|
+
export default class BookmarkFromHistoryModal extends React.PureComponent {
|
|
17
|
+
state = {
|
|
18
|
+
visible: false,
|
|
19
|
+
tab: null,
|
|
20
|
+
selectedCategory: 'default'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
componentDidMount () {
|
|
24
|
+
refsStatic.add('bookmark-from-history-modal', this)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
show (tab) {
|
|
28
|
+
this.setState({
|
|
29
|
+
visible: true,
|
|
30
|
+
tab: copy(tab),
|
|
31
|
+
selectedCategory: 'default'
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
handleClose = () => {
|
|
36
|
+
this.setState({
|
|
37
|
+
visible: false,
|
|
38
|
+
tab: null,
|
|
39
|
+
selectedCategory: 'default'
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
buildBookmark = () => {
|
|
44
|
+
const { tab } = this.state
|
|
45
|
+
if (!tab) return null
|
|
46
|
+
|
|
47
|
+
const r = {
|
|
48
|
+
...tab,
|
|
49
|
+
id: generate()
|
|
50
|
+
}
|
|
51
|
+
console.log(r)
|
|
52
|
+
delete r.parentId
|
|
53
|
+
delete r.category
|
|
54
|
+
return r
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
handleConfirm = () => {
|
|
58
|
+
const { tab, selectedCategory } = this.state
|
|
59
|
+
if (!tab) {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const { store } = window
|
|
64
|
+
const { addItem } = store
|
|
65
|
+
|
|
66
|
+
// Create bookmark from tab data
|
|
67
|
+
const bookmark = this.buildBookmark()
|
|
68
|
+
|
|
69
|
+
// Add bookmark
|
|
70
|
+
addItem(bookmark, 'bookmarks')
|
|
71
|
+
|
|
72
|
+
// Ensure the bookmark id is registered in its group
|
|
73
|
+
try {
|
|
74
|
+
const groupId = selectedCategory || 'default'
|
|
75
|
+
const group = window.store.bookmarkGroups.find(g => g.id === groupId)
|
|
76
|
+
if (group) {
|
|
77
|
+
group.bookmarkIds = [
|
|
78
|
+
...new Set([...(group.bookmarkIds || []), bookmark.id])
|
|
79
|
+
]
|
|
80
|
+
bookmark.color = group.color
|
|
81
|
+
}
|
|
82
|
+
} catch (err) {
|
|
83
|
+
console.error('Failed to update bookmark group:', err)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
message.success(e('Done'))
|
|
87
|
+
this.handleClose()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
render () {
|
|
91
|
+
const { visible, selectedCategory } = this.state
|
|
92
|
+
|
|
93
|
+
if (!visible) {
|
|
94
|
+
return null
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const bookmark = this.buildBookmark()
|
|
98
|
+
const bookmarkJson = JSON.stringify(bookmark, null, 2)
|
|
99
|
+
|
|
100
|
+
const modalProps = {
|
|
101
|
+
open: visible,
|
|
102
|
+
title: (
|
|
103
|
+
<span>
|
|
104
|
+
<PlusOutlined className='mg1r' />
|
|
105
|
+
{e('bookmarks')}
|
|
106
|
+
</span>
|
|
107
|
+
),
|
|
108
|
+
width: 600,
|
|
109
|
+
onCancel: this.handleClose,
|
|
110
|
+
footer: (
|
|
111
|
+
<div className='custom-modal-footer-buttons'>
|
|
112
|
+
<Button onClick={this.handleClose}>
|
|
113
|
+
{e('cancel')}
|
|
114
|
+
</Button>
|
|
115
|
+
<Button type='primary' onClick={this.handleConfirm}>
|
|
116
|
+
{e('confirm')}
|
|
117
|
+
</Button>
|
|
118
|
+
</div>
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<Modal {...modalProps}>
|
|
124
|
+
<div className='bookmark-from-history-modal pd2'>
|
|
125
|
+
<div className='pd1b'>
|
|
126
|
+
<AICategorySelect
|
|
127
|
+
bookmarkGroups={window.store.bookmarkGroups}
|
|
128
|
+
value={selectedCategory}
|
|
129
|
+
onChange={(val) => this.setState({ selectedCategory: val })}
|
|
130
|
+
/>
|
|
131
|
+
</div>
|
|
132
|
+
<div className='pd1b'>
|
|
133
|
+
<pre className='bookmark-json-preview'>
|
|
134
|
+
{bookmarkJson}
|
|
135
|
+
</pre>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
</Modal>
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Tree,
|
|
3
3
|
Button,
|
|
4
|
-
Space
|
|
4
|
+
Space,
|
|
5
|
+
Input
|
|
5
6
|
} from 'antd'
|
|
7
|
+
import { useState, useMemo } from 'react'
|
|
6
8
|
import { defaultBookmarkGroupId, settingMap } from '../../common/constants'
|
|
7
9
|
import deepCopy from 'json-deep-copy'
|
|
8
|
-
import { createTitleWithTag } from '../../common/create-title'
|
|
10
|
+
import createTitle, { createTitleWithTag } from '../../common/create-title'
|
|
9
11
|
|
|
10
12
|
const e = window.translate
|
|
11
13
|
|
|
12
|
-
function buildData (bookmarks, bookmarkGroups) {
|
|
14
|
+
function buildData (bookmarks, bookmarkGroups, searchText = '') {
|
|
15
|
+
const searchLower = searchText.toLowerCase()
|
|
13
16
|
const cats = bookmarkGroups
|
|
14
17
|
const tree = bookmarks
|
|
15
18
|
.reduce((p, k) => {
|
|
@@ -25,6 +28,14 @@ function buildData (bookmarks, bookmarkGroups) {
|
|
|
25
28
|
[k.id]: k
|
|
26
29
|
}
|
|
27
30
|
}, {})
|
|
31
|
+
|
|
32
|
+
// Helper to check if a node matches the search
|
|
33
|
+
function matchesSearch (text) {
|
|
34
|
+
if (!searchText) return true
|
|
35
|
+
const str = String(text || '')
|
|
36
|
+
return str.toLowerCase().includes(searchLower)
|
|
37
|
+
}
|
|
38
|
+
|
|
28
39
|
function buildSubCats (id) {
|
|
29
40
|
const x = btree[id]
|
|
30
41
|
if (!x) {
|
|
@@ -42,6 +53,14 @@ function buildData (bookmarks, bookmarkGroups) {
|
|
|
42
53
|
if (y.children && !y.children.length) {
|
|
43
54
|
delete y.children
|
|
44
55
|
}
|
|
56
|
+
// Filter: include node if it matches or has matching children
|
|
57
|
+
if (searchText) {
|
|
58
|
+
const titleMatches = matchesSearch(x.title || '')
|
|
59
|
+
const hasMatchingChildren = y.children && y.children.length > 0
|
|
60
|
+
if (!titleMatches && !hasMatchingChildren) {
|
|
61
|
+
return ''
|
|
62
|
+
}
|
|
63
|
+
}
|
|
45
64
|
return y
|
|
46
65
|
}
|
|
47
66
|
function buildLeaf (id) {
|
|
@@ -49,6 +68,11 @@ function buildData (bookmarks, bookmarkGroups) {
|
|
|
49
68
|
if (!x) {
|
|
50
69
|
return ''
|
|
51
70
|
}
|
|
71
|
+
const titleText = createTitle(x)
|
|
72
|
+
// Filter: only include leaf if it matches search
|
|
73
|
+
if (searchText && !matchesSearch(titleText)) {
|
|
74
|
+
return ''
|
|
75
|
+
}
|
|
52
76
|
return {
|
|
53
77
|
value: x.id,
|
|
54
78
|
key: x.id,
|
|
@@ -57,14 +81,26 @@ function buildData (bookmarks, bookmarkGroups) {
|
|
|
57
81
|
}
|
|
58
82
|
const level1 = cats.filter(d => d.level !== 2)
|
|
59
83
|
.map(d => {
|
|
84
|
+
const children = [
|
|
85
|
+
...(d.bookmarkGroupIds || []).map(buildSubCats),
|
|
86
|
+
...(d.bookmarkIds || []).map(buildLeaf)
|
|
87
|
+
].filter(d => d)
|
|
88
|
+
// Filter: include group if it matches or has matching children
|
|
89
|
+
if (searchText) {
|
|
90
|
+
const titleMatches = matchesSearch(d.title || '')
|
|
91
|
+
const hasMatchingChildren = children.length > 0
|
|
92
|
+
if (!titleMatches && !hasMatchingChildren) {
|
|
93
|
+
return null
|
|
94
|
+
}
|
|
95
|
+
}
|
|
60
96
|
const r = {
|
|
61
97
|
title: d.title,
|
|
62
98
|
value: d.id,
|
|
63
99
|
key: d.id,
|
|
64
|
-
children
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
100
|
+
children
|
|
101
|
+
}
|
|
102
|
+
if (r.children && !r.children.length) {
|
|
103
|
+
delete r.children
|
|
68
104
|
}
|
|
69
105
|
return r
|
|
70
106
|
}).filter(d => d)
|
|
@@ -72,18 +108,13 @@ function buildData (bookmarks, bookmarkGroups) {
|
|
|
72
108
|
}
|
|
73
109
|
|
|
74
110
|
export default function BookmarkTreeSelect (props) {
|
|
75
|
-
const {
|
|
76
|
-
|
|
77
|
-
const expandedKeys = propExpandedKeys !== undefined ? propExpandedKeys : window.store.expandedKeys
|
|
78
|
-
const checkedKeys = propCheckedKeys !== undefined ? propCheckedKeys : window.store.checkedKeys
|
|
111
|
+
const { bookmarks, bookmarkGroups, type = 'delete', expandedKeys: propExpandedKeys, checkedKeys: propCheckedKeys } = props
|
|
79
112
|
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
113
|
+
const [expandedKeys, setExpandedKeys] = useState(() => deepCopy(propExpandedKeys || []))
|
|
114
|
+
const [checkedKeys, setCheckedKeys] = useState(() => deepCopy(propCheckedKeys || []))
|
|
115
|
+
const [searchText, setSearchText] = useState('')
|
|
83
116
|
|
|
84
|
-
const onCheck =
|
|
85
|
-
window.store.checkedKeys = deepCopy(checkedKeys)
|
|
86
|
-
})
|
|
117
|
+
const onCheck = setCheckedKeys
|
|
87
118
|
|
|
88
119
|
const handleOperation = () => {
|
|
89
120
|
const { store } = window
|
|
@@ -97,7 +128,7 @@ export default function BookmarkTreeSelect (props) {
|
|
|
97
128
|
props.onClose()
|
|
98
129
|
}
|
|
99
130
|
}
|
|
100
|
-
|
|
131
|
+
setCheckedKeys([])
|
|
101
132
|
}
|
|
102
133
|
|
|
103
134
|
const handleCancel = () => {
|
|
@@ -107,7 +138,14 @@ export default function BookmarkTreeSelect (props) {
|
|
|
107
138
|
} else {
|
|
108
139
|
store.bookmarkSelectMode = false
|
|
109
140
|
}
|
|
110
|
-
|
|
141
|
+
setCheckedKeys([])
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const treeData = useMemo(() => buildData(bookmarks, bookmarkGroups, searchText), [bookmarks, bookmarkGroups, searchText])
|
|
145
|
+
|
|
146
|
+
// Auto expand parent nodes when searching
|
|
147
|
+
const handleExpand = (keys) => {
|
|
148
|
+
setExpandedKeys(keys)
|
|
111
149
|
}
|
|
112
150
|
|
|
113
151
|
const rProps = {
|
|
@@ -116,13 +154,22 @@ export default function BookmarkTreeSelect (props) {
|
|
|
116
154
|
onCheck,
|
|
117
155
|
expandedKeys,
|
|
118
156
|
checkedKeys,
|
|
119
|
-
onExpand,
|
|
120
|
-
treeData
|
|
157
|
+
onExpand: handleExpand,
|
|
158
|
+
treeData
|
|
121
159
|
}
|
|
122
160
|
const len = checkedKeys.length
|
|
123
161
|
return (
|
|
124
|
-
<div>
|
|
125
|
-
<div className='
|
|
162
|
+
<div className='tree-select-wrapper pd2'>
|
|
163
|
+
<div className='tree-select-header'>
|
|
164
|
+
<Space.Compact className='mg2b'>
|
|
165
|
+
<Input.Search
|
|
166
|
+
placeholder={e('search') || 'Search...'}
|
|
167
|
+
value={searchText}
|
|
168
|
+
onChange={(e) => setSearchText(e.target.value)}
|
|
169
|
+
allowClear
|
|
170
|
+
style={{ flex: 1 }}
|
|
171
|
+
/>
|
|
172
|
+
</Space.Compact>
|
|
126
173
|
<Space.Compact className='mg2b'>
|
|
127
174
|
<Button
|
|
128
175
|
type='primary'
|
|
@@ -137,6 +184,8 @@ export default function BookmarkTreeSelect (props) {
|
|
|
137
184
|
{e('cancel')}
|
|
138
185
|
</Button>
|
|
139
186
|
</Space.Compact>
|
|
187
|
+
</div>
|
|
188
|
+
<div className='tree-select-content'>
|
|
140
189
|
<Tree {...rProps} />
|
|
141
190
|
</div>
|
|
142
191
|
</div>
|
|
@@ -63,7 +63,6 @@ function handleCopy (element) {
|
|
|
63
63
|
*/
|
|
64
64
|
async function handlePaste (element) {
|
|
65
65
|
if (!isInputElement(element) || element.readOnly || element.disabled) return
|
|
66
|
-
|
|
67
66
|
element.focus()
|
|
68
67
|
const clipboardText = await readClipboardAsync()
|
|
69
68
|
|
|
@@ -89,7 +88,7 @@ function insertTextAtCursor (element, text) {
|
|
|
89
88
|
triggerInputEvents(element)
|
|
90
89
|
}
|
|
91
90
|
} catch (error) {
|
|
92
|
-
console.
|
|
91
|
+
console.error('execCommand insertText failed:', error)
|
|
93
92
|
}
|
|
94
93
|
}
|
|
95
94
|
}
|
|
@@ -135,7 +134,7 @@ function handleCut (element) {
|
|
|
135
134
|
triggerInputEvents(element)
|
|
136
135
|
}
|
|
137
136
|
} catch (error) {
|
|
138
|
-
console.
|
|
137
|
+
console.error('execCommand cut failed:', error)
|
|
139
138
|
}
|
|
140
139
|
}
|
|
141
140
|
}
|
|
@@ -197,12 +196,15 @@ const InputContextMenu = () => {
|
|
|
197
196
|
const [targetElement, setTargetElement] = useState(null)
|
|
198
197
|
const menuRef = useRef(null)
|
|
199
198
|
|
|
200
|
-
const handleMenuClick = async ({ key }) => {
|
|
199
|
+
const handleMenuClick = async ({ key, domEvent }) => {
|
|
200
|
+
domEvent.preventDefault()
|
|
201
|
+
domEvent.stopPropagation()
|
|
201
202
|
// Keep reference to current element before closing menu
|
|
202
203
|
const currentElement = targetElement
|
|
203
204
|
// Close menu first
|
|
204
205
|
setVisible(false)
|
|
205
206
|
setTargetElement(null)
|
|
207
|
+
|
|
206
208
|
// Execute action with preserved element reference
|
|
207
209
|
if (currentElement) {
|
|
208
210
|
setTimeout(async () => {
|
|
@@ -246,7 +248,12 @@ const InputContextMenu = () => {
|
|
|
246
248
|
}
|
|
247
249
|
|
|
248
250
|
const handleClick = (event) => {
|
|
249
|
-
|
|
251
|
+
const isInDropdown = event.target.closest('.ant-dropdown')
|
|
252
|
+
if (
|
|
253
|
+
visible &&
|
|
254
|
+
menuRef.current &&
|
|
255
|
+
!isInDropdown
|
|
256
|
+
) {
|
|
250
257
|
setVisible(false)
|
|
251
258
|
setTargetElement(null)
|
|
252
259
|
}
|
|
@@ -284,6 +291,7 @@ const InputContextMenu = () => {
|
|
|
284
291
|
}}
|
|
285
292
|
open={visible}
|
|
286
293
|
placement='bottomLeft'
|
|
294
|
+
className='input-context-menu'
|
|
287
295
|
>
|
|
288
296
|
<div style={{ width: 1, height: 1 }} />
|
|
289
297
|
</Dropdown>
|
|
@@ -32,6 +32,7 @@ import Opacity from '../common/opacity'
|
|
|
32
32
|
import MoveItemModal from '../tree-list/move-item-modal'
|
|
33
33
|
import InputContextMenu from '../common/input-context-menu'
|
|
34
34
|
import WorkspaceSaveModal from '../tabs/workspace-save-modal'
|
|
35
|
+
import BookmarkFromHistoryModal from '../bookmark-form/bookmark-from-history-modal'
|
|
35
36
|
import { pick } from 'lodash-es'
|
|
36
37
|
import deepCopy from 'json-deep-copy'
|
|
37
38
|
import './wrapper.styl'
|
|
@@ -47,6 +48,7 @@ export default auto(function Index (props) {
|
|
|
47
48
|
ipcOnEvent('open-about', store.openAbout)
|
|
48
49
|
ipcOnEvent('new-ssh', store.onNewSsh)
|
|
49
50
|
ipcOnEvent('add-tab-from-command-line', store.addTabFromCommandLine)
|
|
51
|
+
ipcOnEvent('open-tab', store.addTab)
|
|
50
52
|
ipcOnEvent('openSettings', store.openSetting)
|
|
51
53
|
ipcOnEvent('selectall', store.selectall)
|
|
52
54
|
ipcOnEvent('focused', store.focus)
|
|
@@ -293,6 +295,7 @@ export default auto(function Index (props) {
|
|
|
293
295
|
<TerminalCmdSuggestions {...cmdSuggestionsProps} />
|
|
294
296
|
<TransferQueue />
|
|
295
297
|
<WorkspaceSaveModal store={store} />
|
|
298
|
+
<BookmarkFromHistoryModal />
|
|
296
299
|
<NotificationContainer />
|
|
297
300
|
</div>
|
|
298
301
|
</ConfigProvider>
|
|
@@ -20,6 +20,7 @@ import scanCode from './code-scan'
|
|
|
20
20
|
import resolutions from './resolutions'
|
|
21
21
|
import { readClipboardAsync } from '../../common/clipboard'
|
|
22
22
|
import RemoteFloatControl from '../common/remote-float-control'
|
|
23
|
+
import './rdp.styl'
|
|
23
24
|
|
|
24
25
|
const { Option } = Select
|
|
25
26
|
|
|
@@ -538,7 +539,7 @@ export default class RdpSession extends PureComponent {
|
|
|
538
539
|
}
|
|
539
540
|
return (
|
|
540
541
|
<div
|
|
541
|
-
className='pd1 fix session-v-info'
|
|
542
|
+
className='pd1 fix session-v-info block'
|
|
542
543
|
>
|
|
543
544
|
<div className='fleft'>
|
|
544
545
|
<ReloadOutlined
|
|
@@ -613,14 +614,9 @@ export default class RdpSession extends PureComponent {
|
|
|
613
614
|
tabIndex: 0
|
|
614
615
|
}
|
|
615
616
|
if (scaleViewport) {
|
|
616
|
-
|
|
617
|
-
style: {
|
|
618
|
-
width: '100%',
|
|
619
|
-
objectFit: 'contain'
|
|
620
|
-
}
|
|
621
|
-
})
|
|
617
|
+
canvasProps.className = 'scale-viewport'
|
|
622
618
|
}
|
|
623
|
-
const cls =
|
|
619
|
+
const cls = `rdp-session-wrap session-v-wrap${scaleViewport ? ' scale-viewport' : ''}`
|
|
624
620
|
const controlProps = this.getControlProps()
|
|
625
621
|
return (
|
|
626
622
|
<Spin spinning={loading}>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
.rdp-session-wrap
|
|
2
|
+
display: flex
|
|
3
|
+
flex-direction: column
|
|
4
|
+
justify-content: center
|
|
5
|
+
align-items: center
|
|
6
|
+
.session-v-info
|
|
7
|
+
position: absolute
|
|
8
|
+
top: 0
|
|
9
|
+
left: 0
|
|
10
|
+
width: 100%
|
|
11
|
+
&.scale-viewport
|
|
12
|
+
canvas
|
|
13
|
+
width: 100% !important
|
|
14
|
+
object-fit: contain
|
|
15
|
+
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
.list-item-edit
|
|
7
7
|
.list-item-apply
|
|
8
8
|
.list-item-remove
|
|
9
|
+
.list-item-bookmark
|
|
9
10
|
display none
|
|
10
11
|
width 24px
|
|
11
12
|
line-height 35px
|
|
@@ -13,6 +14,7 @@
|
|
|
13
14
|
position absolute
|
|
14
15
|
right 0
|
|
15
16
|
top 0
|
|
17
|
+
|
|
16
18
|
.list-item-title
|
|
17
19
|
flex-grow: 1
|
|
18
20
|
.item-list-unit
|
|
@@ -32,8 +34,12 @@
|
|
|
32
34
|
.list-item-apply
|
|
33
35
|
.list-item-edit
|
|
34
36
|
.list-item-remove
|
|
37
|
+
.list-item-bookmark
|
|
35
38
|
display block
|
|
36
|
-
.item-list
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
// .item-list
|
|
40
|
+
// .list-item-edit
|
|
41
|
+
// .list-item-apply
|
|
42
|
+
// right 20px
|
|
43
|
+
.item-list-unit
|
|
44
|
+
.list-item-bookmark
|
|
45
|
+
right 18px
|
|
@@ -117,9 +117,9 @@ export default class SettingTerminal extends Component {
|
|
|
117
117
|
return this.saveConfig(data)
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
renderToggle = (name, cls = 'pd2b') => {
|
|
120
|
+
renderToggle = (name, cls = 'pd2b', label) => {
|
|
121
121
|
const checked = !!this.props.config[name]
|
|
122
|
-
const txt = e(name)
|
|
122
|
+
const txt = label || e(name)
|
|
123
123
|
return (
|
|
124
124
|
<div className={cls} key={'rt' + name}>
|
|
125
125
|
<Switch
|
|
@@ -580,6 +580,7 @@ export default class SettingTerminal extends Component {
|
|
|
580
580
|
this.renderToggle('saveTerminalLogToFile')
|
|
581
581
|
}
|
|
582
582
|
{this.renderToggle('addTimeStampToTermLog')}
|
|
583
|
+
{this.renderToggle('enableSixel', 'pd2b', 'SIXEL')}
|
|
583
584
|
{
|
|
584
585
|
[
|
|
585
586
|
'cursorBlink',
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { Component } from 'react'
|
|
6
|
-
import { Dropdown } from 'antd'
|
|
6
|
+
import { Dropdown, Pagination } from 'antd'
|
|
7
7
|
import classnames from 'classnames'
|
|
8
8
|
import FileSection from './file-item'
|
|
9
9
|
import PagedList from './paged-list'
|
|
@@ -37,7 +37,8 @@ export default class FileListTable extends Component {
|
|
|
37
37
|
})
|
|
38
38
|
return {
|
|
39
39
|
pageSize: 100,
|
|
40
|
-
properties
|
|
40
|
+
properties,
|
|
41
|
+
page: 1
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -181,6 +182,29 @@ export default class FileListTable extends Component {
|
|
|
181
182
|
return len > pageSize
|
|
182
183
|
}
|
|
183
184
|
|
|
185
|
+
onPageChange = (page) => {
|
|
186
|
+
this.setState({ page })
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
renderPager () {
|
|
190
|
+
const { page, pageSize } = this.state
|
|
191
|
+
const { fileList } = this.props
|
|
192
|
+
const props = {
|
|
193
|
+
current: page,
|
|
194
|
+
pageSize,
|
|
195
|
+
total: fileList.length,
|
|
196
|
+
showLessItems: true,
|
|
197
|
+
showSizeChanger: false,
|
|
198
|
+
simple: false,
|
|
199
|
+
onChange: this.onPageChange
|
|
200
|
+
}
|
|
201
|
+
return (
|
|
202
|
+
<div className='pd1b pager-wrap'>
|
|
203
|
+
<Pagination {...props} />
|
|
204
|
+
</div>
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
|
|
184
208
|
// reset
|
|
185
209
|
resetWidth = () => {
|
|
186
210
|
this.setState(this.initFromProps())
|
|
@@ -315,9 +339,12 @@ export default class FileListTable extends Component {
|
|
|
315
339
|
list={fileList}
|
|
316
340
|
renderItem={this.renderItem}
|
|
317
341
|
hasPager={hasPager}
|
|
342
|
+
page={this.state.page}
|
|
343
|
+
pageSize={this.state.pageSize}
|
|
318
344
|
/>
|
|
319
345
|
</div>
|
|
320
346
|
</Dropdown>
|
|
347
|
+
{hasPager && this.renderPager()}
|
|
321
348
|
</div>
|
|
322
349
|
)
|
|
323
350
|
}
|
|
@@ -18,9 +18,8 @@ export default class ScrollFiles extends Component {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
renderList () {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
} = this.state
|
|
21
|
+
const page = this.props.page ?? this.state.page
|
|
22
|
+
const pageSize = this.props.pageSize ?? this.state.pageSize
|
|
24
23
|
const start = (page - 1) * pageSize
|
|
25
24
|
const end = start + pageSize
|
|
26
25
|
const {
|
|
@@ -52,10 +51,6 @@ export default class ScrollFiles extends Component {
|
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
render () {
|
|
55
|
-
|
|
56
|
-
if (this.props.hasPager) {
|
|
57
|
-
arr.push(this.renderPager())
|
|
58
|
-
}
|
|
59
|
-
return arr
|
|
54
|
+
return this.renderList()
|
|
60
55
|
}
|
|
61
56
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useRef } from 'react'
|
|
2
2
|
import createTitle, { createTitleWithTag } from '../../common/create-title'
|
|
3
|
-
import { DeleteOutlined } from '@ant-design/icons'
|
|
3
|
+
import { DeleteOutlined, BookFilled } from '@ant-design/icons'
|
|
4
|
+
import { refsStatic } from '../common/ref'
|
|
4
5
|
|
|
5
6
|
export default function HistoryItem (props) {
|
|
6
7
|
const { store } = window
|
|
@@ -31,6 +32,12 @@ export default function HistoryItem (props) {
|
|
|
31
32
|
e.stopPropagation()
|
|
32
33
|
store.history.splice(index, 1)
|
|
33
34
|
}
|
|
35
|
+
|
|
36
|
+
function handleBookmark (e) {
|
|
37
|
+
e.stopPropagation()
|
|
38
|
+
refsStatic.get('bookmark-from-history-modal')?.show(item.tab)
|
|
39
|
+
}
|
|
40
|
+
|
|
34
41
|
const title = createTitleWithTag(item.tab)
|
|
35
42
|
const tt = createTitle(item.tab)
|
|
36
43
|
return (
|
|
@@ -42,6 +49,11 @@ export default function HistoryItem (props) {
|
|
|
42
49
|
<div className='elli pd1y pd2x'>
|
|
43
50
|
{title}
|
|
44
51
|
</div>
|
|
52
|
+
<BookFilled
|
|
53
|
+
className='list-item-bookmark'
|
|
54
|
+
title={window.translate('bookmark')}
|
|
55
|
+
onClick={handleBookmark}
|
|
56
|
+
/>
|
|
45
57
|
<DeleteOutlined
|
|
46
58
|
className='list-item-edit'
|
|
47
59
|
onClick={handleDelete}
|
|
@@ -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}
|