@electerm/electerm-react 1.50.46 → 1.50.65
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/components/batch-op/batch-op.jsx +3 -3
- package/client/components/bookmark-form/render-connection-hopping.jsx +9 -0
- package/client/components/bookmark-form/ssh-form.jsx +3 -0
- package/client/components/bookmark-form/web-form-ui.jsx +29 -1
- package/client/components/footer/batch-input.jsx +96 -20
- package/client/components/footer/batch-item.jsx +33 -0
- package/client/components/footer/footer-entry.jsx +10 -6
- package/client/components/footer/tab-select.jsx +65 -0
- package/client/components/layout/layout.jsx +1 -2
- package/client/components/session/sessions.jsx +32 -33
- package/client/components/terminal/highlight-addon.js +3 -1
- package/client/components/terminal/index.jsx +9 -16
- package/client/components/terminal-info/resource.jsx +9 -0
- package/client/components/web/web-session.jsx +67 -13
- package/client/entry/{index.jsx → electerm.jsx} +1 -1
- package/client/store/watch.js +13 -13
- package/package.json +1 -1
|
@@ -236,7 +236,7 @@ export default class BatchOp extends PureComponent {
|
|
|
236
236
|
this.tm = setTimeout(() => {
|
|
237
237
|
reject(new Error('timeout'))
|
|
238
238
|
}, 1000 * 60 * 60)
|
|
239
|
-
this.ref1 = autoRun(
|
|
239
|
+
this.ref1 = autoRun(() => {
|
|
240
240
|
const { transferHistory } = store
|
|
241
241
|
const first = transferHistory.find(t => {
|
|
242
242
|
return (t.id === obj.id || t.originalId === obj.id) && t.unzip
|
|
@@ -257,7 +257,7 @@ export default class BatchOp extends PureComponent {
|
|
|
257
257
|
return new Promise((resolve, reject) => {
|
|
258
258
|
document.querySelector('.session-current .type-tab.sftp').click()
|
|
259
259
|
const { store } = window
|
|
260
|
-
this.ref2 = autoRun(
|
|
260
|
+
this.ref2 = autoRun(() => {
|
|
261
261
|
const { tabs } = store
|
|
262
262
|
const last = tabs.find(t => t.id === tab.id)
|
|
263
263
|
if (
|
|
@@ -305,7 +305,7 @@ export default class BatchOp extends PureComponent {
|
|
|
305
305
|
}
|
|
306
306
|
const { store } = window
|
|
307
307
|
store.addTab(tab)
|
|
308
|
-
this.ref = autoRun(
|
|
308
|
+
this.ref = autoRun(() => {
|
|
309
309
|
const { tabs } = store
|
|
310
310
|
const len = tabs.length
|
|
311
311
|
const last = tabs[len - 1]
|
|
@@ -112,6 +112,14 @@ export default function renderConnectionHopping (props) {
|
|
|
112
112
|
}
|
|
113
113
|
]
|
|
114
114
|
|
|
115
|
+
function renderPaths () {
|
|
116
|
+
return [
|
|
117
|
+
'👤',
|
|
118
|
+
...list.map(d => d.host),
|
|
119
|
+
form.getFieldValue('host')
|
|
120
|
+
].join(' -> ')
|
|
121
|
+
}
|
|
122
|
+
|
|
115
123
|
function renderList () {
|
|
116
124
|
return (
|
|
117
125
|
<FormItem {...tailFormItemLayout}>
|
|
@@ -127,6 +135,7 @@ export default function renderConnectionHopping (props) {
|
|
|
127
135
|
}
|
|
128
136
|
})}
|
|
129
137
|
/>
|
|
138
|
+
{renderPaths()}
|
|
130
139
|
</FormItem>
|
|
131
140
|
)
|
|
132
141
|
}
|
|
@@ -158,6 +158,9 @@ export default class BookmarkForm extends PureComponent {
|
|
|
158
158
|
item.host = item.host.trim()
|
|
159
159
|
}
|
|
160
160
|
const obj = item
|
|
161
|
+
if (obj.connectionHoppings?.length) {
|
|
162
|
+
obj.hasHopping = true
|
|
163
|
+
}
|
|
161
164
|
const { addItem, editItem } = this.props.store
|
|
162
165
|
const categoryId = obj.category
|
|
163
166
|
delete obj.category
|
|
@@ -6,7 +6,8 @@ import { useEffect } from 'react'
|
|
|
6
6
|
import {
|
|
7
7
|
Input,
|
|
8
8
|
Form,
|
|
9
|
-
TreeSelect
|
|
9
|
+
TreeSelect,
|
|
10
|
+
Switch
|
|
10
11
|
} from 'antd'
|
|
11
12
|
import { formItemLayout } from '../../common/form-layout'
|
|
12
13
|
import {
|
|
@@ -76,6 +77,18 @@ export default function LocalFormUi (props) {
|
|
|
76
77
|
hasFeedback
|
|
77
78
|
name='url'
|
|
78
79
|
required
|
|
80
|
+
rules={[
|
|
81
|
+
{
|
|
82
|
+
required: true,
|
|
83
|
+
message: e('Please input URL')
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
validator: (_, value) =>
|
|
87
|
+
/^[a-z\d.+-]+:\/\/[^\s/$.?#].[^\s]*$/i.test(value)
|
|
88
|
+
? Promise.resolve()
|
|
89
|
+
: Promise.reject(new Error(e('URL must start with http:// or https://')))
|
|
90
|
+
}
|
|
91
|
+
]}
|
|
79
92
|
>
|
|
80
93
|
<Input />
|
|
81
94
|
</FormItem>
|
|
@@ -98,6 +111,21 @@ export default function LocalFormUi (props) {
|
|
|
98
111
|
showSearch
|
|
99
112
|
/>
|
|
100
113
|
</FormItem>
|
|
114
|
+
<FormItem
|
|
115
|
+
{...formItemLayout}
|
|
116
|
+
label={e('useragent')}
|
|
117
|
+
name='useragent'
|
|
118
|
+
>
|
|
119
|
+
<Input />
|
|
120
|
+
</FormItem>
|
|
121
|
+
<FormItem
|
|
122
|
+
{...formItemLayout}
|
|
123
|
+
label='hideAddressBar'
|
|
124
|
+
name='hideAddressBar'
|
|
125
|
+
valuePropName='checked'
|
|
126
|
+
>
|
|
127
|
+
<Switch />
|
|
128
|
+
</FormItem>
|
|
101
129
|
<FormItem
|
|
102
130
|
{...formItemLayout}
|
|
103
131
|
label='type'
|
|
@@ -5,22 +5,44 @@
|
|
|
5
5
|
import { Component } from 'react'
|
|
6
6
|
import {
|
|
7
7
|
AutoComplete,
|
|
8
|
-
Input
|
|
9
|
-
Switch,
|
|
10
|
-
Tooltip
|
|
8
|
+
Input
|
|
11
9
|
} from 'antd'
|
|
12
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
batchInputLsKey,
|
|
12
|
+
commonActions,
|
|
13
|
+
terminalWebType,
|
|
14
|
+
terminalRdpType,
|
|
15
|
+
terminalVncType
|
|
16
|
+
} from '../../common/constants'
|
|
17
|
+
import TabSelect from './tab-select'
|
|
13
18
|
import postMsg from '../../common/post-msg'
|
|
14
19
|
import classNames from 'classnames'
|
|
15
20
|
|
|
16
21
|
const e = window.translate
|
|
17
22
|
|
|
18
23
|
export default class BatchInput extends Component {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
constructor (props) {
|
|
25
|
+
super(props)
|
|
26
|
+
this.state = {
|
|
27
|
+
cmd: '',
|
|
28
|
+
selectedTabIds: [props.currentTabId],
|
|
29
|
+
open: false,
|
|
30
|
+
enter: false
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
componentDidUpdate (prevProps) {
|
|
35
|
+
if (prevProps.currentTabId !== this.props.currentTabId) {
|
|
36
|
+
this.setState(prevState => {
|
|
37
|
+
const newSelectedTabIds = prevState.selectedTabIds.filter(
|
|
38
|
+
id => id !== this.props.currentTabId
|
|
39
|
+
)
|
|
40
|
+
newSelectedTabIds.unshift(this.props.currentTabId)
|
|
41
|
+
return {
|
|
42
|
+
selectedTabIds: newSelectedTabIds
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
}
|
|
24
46
|
}
|
|
25
47
|
|
|
26
48
|
componentWillUnmount () {
|
|
@@ -28,12 +50,12 @@ export default class BatchInput extends Component {
|
|
|
28
50
|
}
|
|
29
51
|
|
|
30
52
|
handleEnter = (e) => {
|
|
31
|
-
const { cmd,
|
|
53
|
+
const { cmd, selectedTabIds } = this.state
|
|
32
54
|
if (!cmd.trim()) {
|
|
33
55
|
return
|
|
34
56
|
}
|
|
35
57
|
window.store.addBatchInput(cmd)
|
|
36
|
-
this.props.input(cmd,
|
|
58
|
+
this.props.input(cmd, selectedTabIds)
|
|
37
59
|
this.setState({
|
|
38
60
|
cmd: '',
|
|
39
61
|
open: false
|
|
@@ -41,6 +63,43 @@ export default class BatchInput extends Component {
|
|
|
41
63
|
e.stopPropagation()
|
|
42
64
|
}
|
|
43
65
|
|
|
66
|
+
onSelectAll = () => {
|
|
67
|
+
this.setState({
|
|
68
|
+
selectedTabIds: this.getTabs().map(tab => tab.id)
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
onSelectNone = () => {
|
|
73
|
+
this.setState({
|
|
74
|
+
selectedTabIds: [this.props.currentTabId]
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
filterValidTabIds = (tabIds) => {
|
|
79
|
+
return tabIds.filter(id => {
|
|
80
|
+
return this.props.tabs.some(tab => tab.id === id)
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
onSelect = (id) => {
|
|
85
|
+
this.setState(prevState => {
|
|
86
|
+
const selectedTabIds = prevState.selectedTabIds.includes(id)
|
|
87
|
+
? prevState.selectedTabIds.filter(tabId => tabId !== id)
|
|
88
|
+
: [...prevState.selectedTabIds, id]
|
|
89
|
+
|
|
90
|
+
// Ensure at least the current tab is selected
|
|
91
|
+
if (selectedTabIds.length === 0) {
|
|
92
|
+
return {
|
|
93
|
+
selectedTabIds: [this.props.currentTabId]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
selectedTabIds
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
|
|
44
103
|
handleChange = (v = '') => {
|
|
45
104
|
let vv = v.replace(/^\d+:/, '').replace(/\n$/, '')
|
|
46
105
|
if (vv === batchInputLsKey) {
|
|
@@ -58,7 +117,8 @@ export default class BatchInput extends Component {
|
|
|
58
117
|
|
|
59
118
|
handleClick = () => {
|
|
60
119
|
this.setState({
|
|
61
|
-
open: true
|
|
120
|
+
open: true,
|
|
121
|
+
selectedTabIds: this.filterValidTabIds(this.state.selectedTabIds)
|
|
62
122
|
})
|
|
63
123
|
}
|
|
64
124
|
|
|
@@ -116,8 +176,22 @@ export default class BatchInput extends Component {
|
|
|
116
176
|
this.timer = setTimeout(this.leave, 5000)
|
|
117
177
|
}
|
|
118
178
|
|
|
179
|
+
getTabs = () => {
|
|
180
|
+
const { currentTabId } = this.props
|
|
181
|
+
return this.props.tabs.filter(tab => {
|
|
182
|
+
return tab.type !== terminalWebType &&
|
|
183
|
+
tab.type !== terminalRdpType &&
|
|
184
|
+
tab.type !== terminalVncType
|
|
185
|
+
}).sort((a, b) => {
|
|
186
|
+
// Current tab goes first
|
|
187
|
+
if (a.id === currentTabId) return -1
|
|
188
|
+
if (b.id === currentTabId) return 1
|
|
189
|
+
return 0
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
|
|
119
193
|
render () {
|
|
120
|
-
const { cmd, open,
|
|
194
|
+
const { cmd, open, selectedTabIds, enter } = this.state
|
|
121
195
|
const opts = {
|
|
122
196
|
options: this.buildOptions(),
|
|
123
197
|
placeholder: e('batchInput'),
|
|
@@ -139,6 +213,14 @@ export default class BatchInput extends Component {
|
|
|
139
213
|
placeholder: e('batchInput'),
|
|
140
214
|
className: 'batch-input-holder'
|
|
141
215
|
}
|
|
216
|
+
const tabSelectProps = {
|
|
217
|
+
currentTabId: this.props.currentTabId,
|
|
218
|
+
tabs: this.getTabs(),
|
|
219
|
+
selectedTabIds,
|
|
220
|
+
onSelectAll: this.onSelectAll,
|
|
221
|
+
onSelectNone: this.onSelectNone,
|
|
222
|
+
onSelect: this.onSelect
|
|
223
|
+
}
|
|
142
224
|
return (
|
|
143
225
|
<span
|
|
144
226
|
className={cls}
|
|
@@ -162,13 +244,7 @@ export default class BatchInput extends Component {
|
|
|
162
244
|
autoSize={{ minRows: 1 }}
|
|
163
245
|
/>
|
|
164
246
|
</AutoComplete>
|
|
165
|
-
<
|
|
166
|
-
<Switch
|
|
167
|
-
className='mg1l'
|
|
168
|
-
checked={toAll}
|
|
169
|
-
onChange={this.handleChangeAll}
|
|
170
|
-
/>
|
|
171
|
-
</Tooltip>
|
|
247
|
+
<TabSelect {...tabSelectProps} />
|
|
172
248
|
</span>
|
|
173
249
|
</span>
|
|
174
250
|
)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Button
|
|
3
|
+
} from 'antd'
|
|
4
|
+
import {
|
|
5
|
+
CheckCircleOutlined
|
|
6
|
+
} from '@ant-design/icons'
|
|
7
|
+
import createName from '../../common/create-title'
|
|
8
|
+
|
|
9
|
+
export default function BatchInputTabItem (props) {
|
|
10
|
+
function handleSelect (id) {
|
|
11
|
+
props.onSelect(
|
|
12
|
+
props.id
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { tab, selected, isCurrent } = props
|
|
17
|
+
const title = createName(tab)
|
|
18
|
+
const btnProps = {
|
|
19
|
+
className: 'mg1r mg1b iblock',
|
|
20
|
+
onClick: handleSelect,
|
|
21
|
+
title,
|
|
22
|
+
type: selected ? 'primary' : 'default'
|
|
23
|
+
}
|
|
24
|
+
const icon = selected ? <CheckCircleOutlined className='mg1r' /> : null
|
|
25
|
+
const pre = isCurrent ? <b>*</b> : ''
|
|
26
|
+
return (
|
|
27
|
+
<Button
|
|
28
|
+
{...btnProps}
|
|
29
|
+
>
|
|
30
|
+
{pre} {icon} {tab.tabCount}. {title}
|
|
31
|
+
</Button>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -26,11 +26,10 @@ export default auto(function FooterEntry (props) {
|
|
|
26
26
|
})
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function batchInput (cmd,
|
|
29
|
+
function batchInput (cmd, selectedTabIds) {
|
|
30
30
|
postMessage({
|
|
31
31
|
action: terminalActions.batchInput,
|
|
32
|
-
|
|
33
|
-
toAll,
|
|
32
|
+
selectedTabIds,
|
|
34
33
|
cmd
|
|
35
34
|
})
|
|
36
35
|
}
|
|
@@ -55,11 +54,16 @@ export default auto(function FooterEntry (props) {
|
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
function renderBatchInputs () {
|
|
57
|
+
const batchProps = {
|
|
58
|
+
input: batchInput,
|
|
59
|
+
batchInputs: props.store.batchInputs,
|
|
60
|
+
tabs: props.store.tabs,
|
|
61
|
+
currentTabId: props.store.currentTabId
|
|
62
|
+
}
|
|
58
63
|
return (
|
|
59
64
|
<div className='terminal-footer-unit terminal-footer-center'>
|
|
60
65
|
<BatchInput
|
|
61
|
-
|
|
62
|
-
batchInputs={props.store.batchInputs}
|
|
66
|
+
{...batchProps}
|
|
63
67
|
/>
|
|
64
68
|
</div>
|
|
65
69
|
)
|
|
@@ -79,7 +83,7 @@ export default auto(function FooterEntry (props) {
|
|
|
79
83
|
minWidth: 30
|
|
80
84
|
},
|
|
81
85
|
placeholder: e('encode'),
|
|
82
|
-
defaultValue: props.currentTab?.encode,
|
|
86
|
+
defaultValue: props.store.currentTab?.encode,
|
|
83
87
|
onSelect: handleSwitchEncoding,
|
|
84
88
|
size: 'small',
|
|
85
89
|
popupMatchSelectWidth: false
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Popover
|
|
3
|
+
} from 'antd'
|
|
4
|
+
import TabItem from './batch-item'
|
|
5
|
+
import {
|
|
6
|
+
CodeOutlined
|
|
7
|
+
} from '@ant-design/icons'
|
|
8
|
+
|
|
9
|
+
export default function TabSelect (props) {
|
|
10
|
+
const { selectedTabIds, tabs, currentTabId } = props
|
|
11
|
+
function renderTabs () {
|
|
12
|
+
return tabs.map(tab => {
|
|
13
|
+
const selected = selectedTabIds.includes(tab.id)
|
|
14
|
+
const itemProps = {
|
|
15
|
+
tab,
|
|
16
|
+
selected,
|
|
17
|
+
onSelect: props.onSelect,
|
|
18
|
+
id: tab.id,
|
|
19
|
+
isCurrent: tab.id === currentTabId
|
|
20
|
+
}
|
|
21
|
+
return (
|
|
22
|
+
<TabItem
|
|
23
|
+
key={tab.id}
|
|
24
|
+
{...itemProps}
|
|
25
|
+
/>
|
|
26
|
+
)
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
function renderBtns () {
|
|
30
|
+
return (
|
|
31
|
+
<div className='pd1t pd2b font12'>
|
|
32
|
+
<span
|
|
33
|
+
className='mg1r pointer'
|
|
34
|
+
onClick={props.onSelectAll}
|
|
35
|
+
>
|
|
36
|
+
All
|
|
37
|
+
</span>
|
|
38
|
+
<span
|
|
39
|
+
className='pointer'
|
|
40
|
+
onClick={props.onSelectNone}
|
|
41
|
+
>
|
|
42
|
+
None
|
|
43
|
+
</span>
|
|
44
|
+
</div>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
function renderContent () {
|
|
48
|
+
return (
|
|
49
|
+
<div className='pd1x'>
|
|
50
|
+
{renderBtns()}
|
|
51
|
+
{renderTabs()}
|
|
52
|
+
</div>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
return (
|
|
56
|
+
<Popover
|
|
57
|
+
content={renderContent()}
|
|
58
|
+
trigger='click'
|
|
59
|
+
>
|
|
60
|
+
<span className='pointer iblock pd1x'>
|
|
61
|
+
({selectedTabIds.length}) <CodeOutlined />
|
|
62
|
+
</span>
|
|
63
|
+
</Popover>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
@@ -5,7 +5,6 @@ import { findIndex, pick } from 'lodash-es'
|
|
|
5
5
|
import classNames from 'classnames'
|
|
6
6
|
import generate from '../../common/id-with-stamp'
|
|
7
7
|
import copy from 'json-deep-copy'
|
|
8
|
-
import wait from '../../common/wait.js'
|
|
9
8
|
import Tabs from '../tabs/index.jsx'
|
|
10
9
|
import {
|
|
11
10
|
tabActions,
|
|
@@ -146,7 +145,7 @@ class Sessions extends Component {
|
|
|
146
145
|
})
|
|
147
146
|
}
|
|
148
147
|
|
|
149
|
-
addTab = (_tab, _index) => {
|
|
148
|
+
addTab = (_tab, _index, callback) => {
|
|
150
149
|
this.setState((oldState) => {
|
|
151
150
|
const tabs = copy(oldState.tabs)
|
|
152
151
|
const index = typeof _index === 'undefined'
|
|
@@ -167,6 +166,9 @@ class Sessions extends Component {
|
|
|
167
166
|
}, () => {
|
|
168
167
|
this.updateStoreTabs(this.state.tabs)
|
|
169
168
|
this.updateStoreCurrentTabId(this.state.currentTabId)
|
|
169
|
+
if (callback) {
|
|
170
|
+
callback()
|
|
171
|
+
}
|
|
170
172
|
})
|
|
171
173
|
}
|
|
172
174
|
|
|
@@ -207,42 +209,39 @@ class Sessions extends Component {
|
|
|
207
209
|
window.store.currentTabId = this.state.currentTabId
|
|
208
210
|
}
|
|
209
211
|
|
|
210
|
-
reloadTab =
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
this.addTab(tab, index)
|
|
222
|
-
await wait(30)
|
|
212
|
+
reloadTab = (tabToReload) => {
|
|
213
|
+
const tab = copy(
|
|
214
|
+
tabToReload
|
|
215
|
+
)
|
|
216
|
+
tab.pane = paneMap.terminal
|
|
217
|
+
const { id } = tab
|
|
218
|
+
const { tabs } = this.state
|
|
219
|
+
tab.id = generate()
|
|
220
|
+
tab.status = statusMap.processing
|
|
221
|
+
const index = findIndex(tabs, t => t.id === id)
|
|
222
|
+
this.addTab(tab, index, () => {
|
|
223
223
|
this.delTab(id)
|
|
224
|
+
this.onChangeTabId(tab.id)
|
|
224
225
|
})
|
|
225
226
|
}
|
|
226
227
|
|
|
227
228
|
onDuplicateTab = (tabToDup) => {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
tab
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
this.addTab(tab, index + 1)
|
|
245
|
-
})
|
|
229
|
+
const defaultStatus = statusMap.processing
|
|
230
|
+
let tab = copy(tabToDup)
|
|
231
|
+
updateCount(tab)
|
|
232
|
+
const tabs = copy(this.state.tabs)
|
|
233
|
+
const index = findIndex(
|
|
234
|
+
tabs,
|
|
235
|
+
d => d.id === tab.id
|
|
236
|
+
)
|
|
237
|
+
tab = {
|
|
238
|
+
...tab,
|
|
239
|
+
status: defaultStatus,
|
|
240
|
+
id: generate(),
|
|
241
|
+
isTransporting: undefined
|
|
242
|
+
}
|
|
243
|
+
tab.pane = paneMap.terminal
|
|
244
|
+
this.addTab(tab, index + 1)
|
|
246
245
|
}
|
|
247
246
|
|
|
248
247
|
onChangeTabId = id => {
|
|
@@ -37,7 +37,9 @@ export class KeywordHighlighterAddon {
|
|
|
37
37
|
if (keyword) {
|
|
38
38
|
try {
|
|
39
39
|
const regex = new RegExp(`(${keyword})`, 'gi')
|
|
40
|
-
|
|
40
|
+
if (regex.test(text)) {
|
|
41
|
+
return text.replace(regex, this.colorize(color))
|
|
42
|
+
}
|
|
41
43
|
} catch (e) {
|
|
42
44
|
window.store.onError(e)
|
|
43
45
|
}
|
|
@@ -329,15 +329,14 @@ clear\r`
|
|
|
329
329
|
addTimeStampToTermLog,
|
|
330
330
|
type,
|
|
331
331
|
cmd,
|
|
332
|
-
|
|
332
|
+
selectedTabIds = [],
|
|
333
333
|
pid,
|
|
334
|
-
toAll,
|
|
335
334
|
inputOnly,
|
|
336
335
|
zoomValue
|
|
337
336
|
} = e?.data || {}
|
|
338
337
|
|
|
339
338
|
const { id: currentTabIdProp } = this.props.tab
|
|
340
|
-
const tabIdMatch =
|
|
339
|
+
const tabIdMatch = selectedTabIds.includes(currentTabIdProp)
|
|
341
340
|
if (
|
|
342
341
|
action === terminalActions.zoom &&
|
|
343
342
|
tabIdMatch
|
|
@@ -351,7 +350,7 @@ clear\r`
|
|
|
351
350
|
} else if (
|
|
352
351
|
action === terminalActions.batchInput &&
|
|
353
352
|
(
|
|
354
|
-
|
|
353
|
+
tabIdMatch
|
|
355
354
|
)
|
|
356
355
|
) {
|
|
357
356
|
this.batchInput(cmd)
|
|
@@ -1220,22 +1219,16 @@ clear\r`
|
|
|
1220
1219
|
if (pick(term, 'buffer._onBufferChange._listeners')) {
|
|
1221
1220
|
term.buffer._onBufferChange._listeners.push(this.onBufferChange)
|
|
1222
1221
|
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
this.fitAddon.fit()
|
|
1230
|
-
term.loadAddon(this.zmodemAddon)
|
|
1231
|
-
term.zmodemAttach(this)
|
|
1232
|
-
}
|
|
1222
|
+
term.loadAddon(new WebLinksAddon(this.webLinkHandler))
|
|
1223
|
+
term.focus()
|
|
1224
|
+
this.zmodemAddon = new AddonZmodem()
|
|
1225
|
+
this.fitAddon.fit()
|
|
1226
|
+
term.loadAddon(this.zmodemAddon)
|
|
1227
|
+
term.zmodemAttach(this)
|
|
1233
1228
|
term.displayRaw = displayRaw
|
|
1234
1229
|
term.loadAddon(
|
|
1235
1230
|
new KeywordHighlighterAddon(keywords)
|
|
1236
1231
|
)
|
|
1237
|
-
window.store.triggerResize()
|
|
1238
|
-
window.store.focus()
|
|
1239
1232
|
}
|
|
1240
1233
|
|
|
1241
1234
|
onResize = throttle(() => {
|
|
@@ -34,6 +34,13 @@ export default function TerminalInfoResource (props) {
|
|
|
34
34
|
) {
|
|
35
35
|
return null
|
|
36
36
|
}
|
|
37
|
+
function getColorForPercent (percent) {
|
|
38
|
+
if (percent >= 90) return '#ff4d4f'
|
|
39
|
+
if (percent >= 70) return '#faad14'
|
|
40
|
+
if (percent >= 50) return '#1890ff'
|
|
41
|
+
return '#52c41a'
|
|
42
|
+
}
|
|
43
|
+
|
|
37
44
|
function renderItem (obj) {
|
|
38
45
|
if (isEmpty(obj)) {
|
|
39
46
|
return <div className='pd1b' key={obj.name}>NA</div>
|
|
@@ -48,6 +55,7 @@ export default function TerminalInfoResource (props) {
|
|
|
48
55
|
const p = hasPercent
|
|
49
56
|
? percent
|
|
50
57
|
: computePercent(used, total) || 0
|
|
58
|
+
const color = getColorForPercent(p)
|
|
51
59
|
const fmt = hasPercent
|
|
52
60
|
? (p) => `${name}: ${p || ''}%`
|
|
53
61
|
: (p) => `${name}: ${p || ''}%(${used || ''}/${total || ''})`
|
|
@@ -57,6 +65,7 @@ export default function TerminalInfoResource (props) {
|
|
|
57
65
|
style={{ width: '50%' }}
|
|
58
66
|
percent={p}
|
|
59
67
|
format={fmt}
|
|
68
|
+
strokeColor={color}
|
|
60
69
|
/>
|
|
61
70
|
</div>
|
|
62
71
|
)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import AddressBar from './address-bar'
|
|
2
|
+
// import React, { useEffect } from 'react'
|
|
2
3
|
|
|
3
4
|
export default function WebSession (props) {
|
|
4
5
|
const {
|
|
@@ -7,8 +8,11 @@ export default function WebSession (props) {
|
|
|
7
8
|
height,
|
|
8
9
|
reloadTab
|
|
9
10
|
} = props
|
|
11
|
+
const urlRegex = /^[a-z\d.+-]+:\/\/[^\s/$.?#].[^\s]*$/i
|
|
12
|
+
|
|
13
|
+
const { url = '' } = tab
|
|
10
14
|
const addrProps = {
|
|
11
|
-
url
|
|
15
|
+
url,
|
|
12
16
|
title: tab.title,
|
|
13
17
|
description: tab.description,
|
|
14
18
|
onOpen: () => {
|
|
@@ -20,22 +24,72 @@ export default function WebSession (props) {
|
|
|
20
24
|
)
|
|
21
25
|
}
|
|
22
26
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
|
|
28
|
+
// TODO: 支持自定义Header和Cookie
|
|
29
|
+
// useEffect(() => {
|
|
30
|
+
// const webview = document.querySelector('webview')
|
|
31
|
+
// if (webview) {
|
|
32
|
+
// // 添加事件监听,输出所有的事件
|
|
33
|
+
// webview.addEventListener('did-start-loading', (e) => {
|
|
34
|
+
// console.log('did-start-loading', e)
|
|
35
|
+
// })
|
|
36
|
+
// }
|
|
37
|
+
// }, []);
|
|
38
|
+
|
|
39
|
+
// 打开webview的开发者工具
|
|
40
|
+
// useEffect(() => {
|
|
41
|
+
// const webview = document.querySelector('webview')
|
|
42
|
+
// if (webview) {
|
|
43
|
+
// webview.addEventListener('dom-ready', () => {
|
|
44
|
+
// webview.openDevTools()
|
|
45
|
+
// })
|
|
46
|
+
// }
|
|
47
|
+
// }, [])
|
|
48
|
+
|
|
49
|
+
function renderView () {
|
|
50
|
+
if (!urlRegex.test(tab.url)) {
|
|
51
|
+
return (
|
|
52
|
+
<div>
|
|
53
|
+
URL: <b>{url}</b> not valid
|
|
54
|
+
</div>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
const hOffset = tab.hideAddressBar ? 30 : -12
|
|
58
|
+
if (window.et.isWebApp) {
|
|
59
|
+
const iframeProps = {
|
|
60
|
+
src: url,
|
|
61
|
+
style: {
|
|
62
|
+
width: (width - 10) + 'px',
|
|
63
|
+
height: (height + hOffset) + 'px'
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return (
|
|
67
|
+
<iframe {...iframeProps} />
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
const viewProps = {
|
|
71
|
+
src: url,
|
|
72
|
+
style: {
|
|
73
|
+
width: (width - 10) + 'px',
|
|
74
|
+
height: (height + hOffset) + 'px'
|
|
75
|
+
},
|
|
76
|
+
disableblinkfeatures: 'true',
|
|
77
|
+
disablewebsecurity: 'true',
|
|
78
|
+
allowpopups: 'true',
|
|
79
|
+
useragent: tab.useragent || 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
|
|
80
|
+
}
|
|
81
|
+
return (
|
|
82
|
+
<webview {...viewProps} />
|
|
83
|
+
)
|
|
31
84
|
}
|
|
85
|
+
|
|
32
86
|
return (
|
|
33
87
|
<div className='web-session-wrap'>
|
|
34
|
-
|
|
88
|
+
{!tab.hideAddressBar && (
|
|
89
|
+
<AddressBar {...addrProps} />
|
|
90
|
+
)}
|
|
35
91
|
<div className='pd1'>
|
|
36
|
-
|
|
37
|
-
{...viewProps}
|
|
38
|
-
/>
|
|
92
|
+
{renderView()}
|
|
39
93
|
</div>
|
|
40
94
|
</div>
|
|
41
95
|
)
|
package/client/store/watch.js
CHANGED
|
@@ -18,14 +18,14 @@ import * as ls from '../common/safe-local-storage'
|
|
|
18
18
|
import { debounce, isEmpty } from 'lodash-es'
|
|
19
19
|
|
|
20
20
|
export default store => {
|
|
21
|
-
// autoRun(
|
|
21
|
+
// autoRun(() => {
|
|
22
22
|
// store.focus()
|
|
23
23
|
// // store.termSearchOpen = false
|
|
24
24
|
// store.termSearchMatchCount = 0
|
|
25
25
|
// return store.currentTabId
|
|
26
26
|
// }).start()
|
|
27
27
|
|
|
28
|
-
// autoRun(
|
|
28
|
+
// autoRun(() => {
|
|
29
29
|
// if (store.menuOpened) {
|
|
30
30
|
// store.initMenuEvent()
|
|
31
31
|
// } else {
|
|
@@ -35,7 +35,7 @@ export default store => {
|
|
|
35
35
|
// })
|
|
36
36
|
|
|
37
37
|
for (const name of dbNamesForWatch) {
|
|
38
|
-
autoRun(
|
|
38
|
+
autoRun(async () => {
|
|
39
39
|
await update(
|
|
40
40
|
`${name}:order`,
|
|
41
41
|
store.getItems(name).map(d => d.id)
|
|
@@ -48,12 +48,12 @@ export default store => {
|
|
|
48
48
|
}, func => debounce(func, 100)).start()
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
autoRun(
|
|
51
|
+
autoRun(async () => {
|
|
52
52
|
ls.setItem(resolutionsLsKey, store._resolutions)
|
|
53
53
|
return store._resolutions
|
|
54
54
|
}).start()
|
|
55
55
|
|
|
56
|
-
autoRun(
|
|
56
|
+
autoRun(() => {
|
|
57
57
|
if (!store.showModal) {
|
|
58
58
|
store.focus()
|
|
59
59
|
} else {
|
|
@@ -62,44 +62,44 @@ export default store => {
|
|
|
62
62
|
return store.showModal
|
|
63
63
|
}).start()
|
|
64
64
|
|
|
65
|
-
autoRun(
|
|
65
|
+
autoRun(() => {
|
|
66
66
|
if (!isEmpty(store.config)) {
|
|
67
67
|
window.pre.runGlobalAsync('saveUserConfig', store.config)
|
|
68
68
|
}
|
|
69
69
|
return store._config
|
|
70
70
|
}, func => debounce(func, 100)).start()
|
|
71
71
|
|
|
72
|
-
autoRun(
|
|
72
|
+
autoRun(() => {
|
|
73
73
|
store.updateLastDataUpdateTime()
|
|
74
74
|
return store.config.theme
|
|
75
75
|
}, func => debounce(func, 100)).start()
|
|
76
76
|
|
|
77
|
-
autoRun(
|
|
77
|
+
autoRun(() => {
|
|
78
78
|
store.updateTabsStatus()
|
|
79
79
|
return store.fileTransfers
|
|
80
80
|
}, func => debounce(func, 100)).start()
|
|
81
81
|
|
|
82
|
-
autoRun(
|
|
82
|
+
autoRun(() => {
|
|
83
83
|
ls.setItemJSON(sftpDefaultSortSettingKey, store.sftpSortSetting)
|
|
84
84
|
return store._sftpSortSetting
|
|
85
85
|
}).start()
|
|
86
86
|
|
|
87
|
-
autoRun(
|
|
87
|
+
autoRun(() => {
|
|
88
88
|
ls.setItemJSON(expandedKeysLsKey, store.expandedKeys)
|
|
89
89
|
return store._expandedKeys
|
|
90
90
|
}).start()
|
|
91
91
|
|
|
92
|
-
autoRun(
|
|
92
|
+
autoRun(() => {
|
|
93
93
|
ls.setItemJSON(localAddrBookmarkLsKey, store.addressBookmarksLocal)
|
|
94
94
|
return store._addressBookmarksLocal
|
|
95
95
|
}).start()
|
|
96
96
|
|
|
97
|
-
autoRun(
|
|
97
|
+
autoRun(() => {
|
|
98
98
|
ls.setItemJSON(checkedKeysLsKey, store.checkedKeys)
|
|
99
99
|
return store._checkedKeys
|
|
100
100
|
}).start()
|
|
101
101
|
|
|
102
|
-
autoRun(
|
|
102
|
+
autoRun(() => {
|
|
103
103
|
const tabs = store.getTabs()
|
|
104
104
|
const { currentTabId } = store
|
|
105
105
|
const tab = tabs.find(t => t.id === currentTabId)
|