@electerm/electerm-react 3.1.26 → 3.3.8
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 +1 -3
- package/client/common/db.js +4 -2
- package/client/components/ai/ai-history.jsx +4 -4
- package/client/components/batch-op/batch-op-alert.jsx +42 -0
- package/client/components/batch-op/batch-op-editor.jsx +202 -0
- package/client/components/batch-op/batch-op-logs.jsx +53 -0
- package/client/components/batch-op/batch-op-runner.jsx +315 -0
- package/client/components/bookmark-form/ai-bookmark-form.jsx +2 -1
- package/client/components/bookmark-form/bookmark-from-history-modal.jsx +2 -1
- package/client/components/bookmark-form/common/bookmark-select.jsx +18 -2
- package/client/components/bookmark-form/common/connection-hopping-form.jsx +153 -0
- package/client/components/bookmark-form/common/connection-hopping.jsx +136 -129
- package/client/components/common/auto-check-update.jsx +31 -0
- package/client/components/common/notification.styl +1 -1
- package/client/components/file-transfer/conflict-resolve.jsx +3 -0
- package/client/components/footer/batch-input.jsx +10 -7
- package/client/components/main/error-wrapper.jsx +18 -7
- package/client/components/main/main.jsx +6 -7
- package/client/components/quick-commands/qm.styl +0 -2
- package/client/components/quick-commands/quick-commands-list-form.jsx +1 -1
- package/client/components/setting-panel/hotkey.jsx +9 -1
- package/client/components/setting-panel/list.jsx +0 -1
- package/client/components/setting-panel/list.styl +4 -0
- package/client/components/setting-panel/setting-modal.jsx +53 -47
- package/client/components/setting-sync/auto-sync.jsx +53 -0
- package/client/components/setting-sync/data-import.jsx +69 -8
- package/client/components/sftp/address-bar.jsx +7 -1
- package/client/components/shortcuts/shortcut-editor.jsx +4 -2
- package/client/components/sidebar/bookmark-select.jsx +3 -2
- package/client/components/sidebar/history-item.jsx +3 -1
- package/client/components/sidebar/history.jsx +1 -0
- package/client/components/sidebar/index.jsx +0 -9
- package/client/components/tabs/add-btn-menu.jsx +1 -1
- package/client/components/tabs/add-btn.jsx +9 -15
- package/client/components/tabs/quick-connect.jsx +6 -10
- package/client/components/terminal/attach-addon-custom.js +86 -0
- package/client/components/terminal/cmd-item.jsx +13 -3
- package/client/components/terminal/drop-file-modal.jsx +57 -0
- package/client/components/terminal/terminal-command-dropdown.jsx +91 -13
- package/client/components/terminal/terminal.jsx +107 -10
- package/client/components/terminal/terminal.styl +9 -0
- package/client/components/tree-list/tree-list-item.jsx +0 -1
- package/client/components/tree-list/tree-list.jsx +115 -10
- package/client/components/tree-list/tree-list.styl +3 -0
- package/client/components/tree-list/tree-search.jsx +9 -1
- package/client/components/vnc/vnc-session.jsx +2 -0
- package/client/components/widgets/widget-control.jsx +3 -0
- package/client/components/widgets/widget-form.jsx +6 -0
- package/client/components/widgets/widget-instance.jsx +26 -7
- package/client/css/includes/box.styl +3 -0
- package/client/store/common.js +0 -28
- package/client/store/init-state.js +2 -1
- package/client/store/load-data.js +6 -4
- package/client/store/mcp-handler.js +20 -2
- package/client/store/sync.js +25 -1
- package/client/store/tab.js +1 -1
- package/client/store/watch.js +10 -18
- package/client/store/widgets.js +54 -0
- package/client/views/index.pug +1 -2
- package/package.json +1 -1
- package/client/components/batch-op/batch-op.jsx +0 -694
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* tree list for bookmarks
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import React from 'react'
|
|
5
6
|
import { Component } from 'manate/react/class-components'
|
|
6
7
|
import {
|
|
7
8
|
CheckOutlined,
|
|
@@ -44,6 +45,7 @@ export default class ItemListTree extends Component {
|
|
|
44
45
|
categoryColor: '',
|
|
45
46
|
categoryId: ''
|
|
46
47
|
}
|
|
48
|
+
this.treeRef = React.createRef()
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
onSubmit = false
|
|
@@ -102,9 +104,55 @@ export default class ItemListTree extends Component {
|
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
handleChange = keyword => {
|
|
105
|
-
this.setState({
|
|
106
|
-
|
|
107
|
+
this.setState({ keyword })
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
handleKeyDown = (e) => {
|
|
111
|
+
const { keyword } = this.state
|
|
112
|
+
if (!keyword) return
|
|
113
|
+
|
|
114
|
+
const treeContainer = this.treeRef.current
|
|
115
|
+
if (!treeContainer) return
|
|
116
|
+
|
|
117
|
+
const allItems = treeContainer.querySelectorAll('.tree-item')
|
|
118
|
+
const matchedItems = Array.from(allItems).filter(item => {
|
|
119
|
+
const isGroup = item.getAttribute('data-is-group') === 'true'
|
|
120
|
+
if (isGroup) return false
|
|
121
|
+
const title = item.querySelector('.tree-item-title')
|
|
122
|
+
if (!title) return false
|
|
123
|
+
const text = title.textContent || ''
|
|
124
|
+
return text.toLowerCase().includes(keyword.toLowerCase())
|
|
107
125
|
})
|
|
126
|
+
|
|
127
|
+
if (matchedItems.length === 0) return
|
|
128
|
+
|
|
129
|
+
const currentSelected = treeContainer.querySelector('.tree-item.search-selected')
|
|
130
|
+
let currentIndex = -1
|
|
131
|
+
if (currentSelected) {
|
|
132
|
+
currentSelected.classList.remove('search-selected')
|
|
133
|
+
currentIndex = matchedItems.indexOf(currentSelected)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (e.key === 'ArrowDown') {
|
|
137
|
+
e.preventDefault()
|
|
138
|
+
const nextIndex = (currentIndex + 1) % matchedItems.length
|
|
139
|
+
matchedItems[nextIndex].classList.add('search-selected')
|
|
140
|
+
matchedItems[nextIndex].scrollIntoView({ block: 'nearest' })
|
|
141
|
+
} else if (e.key === 'ArrowUp') {
|
|
142
|
+
e.preventDefault()
|
|
143
|
+
const nextIndex = currentIndex <= 0 ? matchedItems.length - 1 : currentIndex - 1
|
|
144
|
+
matchedItems[nextIndex].classList.add('search-selected')
|
|
145
|
+
matchedItems[nextIndex].scrollIntoView({ block: 'nearest' })
|
|
146
|
+
} else if (e.key === 'Enter') {
|
|
147
|
+
e.preventDefault()
|
|
148
|
+
const target = currentIndex >= 0 ? matchedItems[currentIndex] : matchedItems[0]
|
|
149
|
+
if (target) {
|
|
150
|
+
const titleEl = target.querySelector('.tree-item-title')
|
|
151
|
+
if (titleEl) {
|
|
152
|
+
titleEl.click()
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
108
156
|
}
|
|
109
157
|
|
|
110
158
|
handleCancelNew = () => {
|
|
@@ -298,8 +346,8 @@ export default class ItemListTree extends Component {
|
|
|
298
346
|
onSelect = (
|
|
299
347
|
e
|
|
300
348
|
) => {
|
|
301
|
-
const id = e.
|
|
302
|
-
const isGroup = e.
|
|
349
|
+
const id = e.currentTarget.getAttribute('data-item-id')
|
|
350
|
+
const isGroup = e.currentTarget.getAttribute('data-is-group') === 'true'
|
|
303
351
|
const { store } = window
|
|
304
352
|
if (isGroup) {
|
|
305
353
|
store.storeAssign({
|
|
@@ -329,6 +377,8 @@ export default class ItemListTree extends Component {
|
|
|
329
377
|
<TreeSearch
|
|
330
378
|
onSearch={this.handleChange}
|
|
331
379
|
keyword={this.state.keyword}
|
|
380
|
+
autoFocus={this.props.autoFocus}
|
|
381
|
+
onKeyDown={this.handleKeyDown}
|
|
332
382
|
/>
|
|
333
383
|
</div>
|
|
334
384
|
)
|
|
@@ -717,6 +767,35 @@ export default class ItemListTree extends Component {
|
|
|
717
767
|
renderGroup = (group, index, parentId) => {
|
|
718
768
|
const pids = typeof parentId === 'string' ? parentId : ''
|
|
719
769
|
const pid = pids + '#' + group.id
|
|
770
|
+
const { bookmarkIds = [], bookmarkGroupIds = [] } = group
|
|
771
|
+
|
|
772
|
+
const hasMatchedItems = (ids) => {
|
|
773
|
+
const tree = this.props.bookmarksMap
|
|
774
|
+
const { keyword } = this.state
|
|
775
|
+
return ids.some(id => {
|
|
776
|
+
const item = tree.get(id)
|
|
777
|
+
return item && createName(item).toLowerCase().includes(keyword.toLowerCase())
|
|
778
|
+
})
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
const hasMatchedSubGroup = (bg) => {
|
|
782
|
+
const bgIds = bg.bookmarkIds || []
|
|
783
|
+
const bgSubIds = bg.bookmarkGroupIds || []
|
|
784
|
+
if (hasMatchedItems(bgIds)) return true
|
|
785
|
+
return bgSubIds.some(sgid => {
|
|
786
|
+
const subBg = window.store.bookmarkGroupTree[sgid]
|
|
787
|
+
return subBg && hasMatchedSubGroup(subBg)
|
|
788
|
+
})
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
if (this.state.keyword) {
|
|
792
|
+
if (!hasMatchedItems(bookmarkIds) && !bookmarkGroupIds.some(id => {
|
|
793
|
+
const sg = window.store.bookmarkGroupTree[id]
|
|
794
|
+
return sg && hasMatchedSubGroup(sg)
|
|
795
|
+
})) {
|
|
796
|
+
return null
|
|
797
|
+
}
|
|
798
|
+
}
|
|
720
799
|
return (
|
|
721
800
|
<div key={group.id} className='group-container'>
|
|
722
801
|
{
|
|
@@ -803,9 +882,14 @@ export default class ItemListTree extends Component {
|
|
|
803
882
|
if (!shouldRender) {
|
|
804
883
|
return null
|
|
805
884
|
}
|
|
885
|
+
const subGroups = this.renderSubGroup(bookmarkGroupIds, parentId)
|
|
886
|
+
const childs = this.renderChilds(bookmarkIds, parentId)
|
|
887
|
+
if (this.state.keyword && subGroups.length === 0 && childs.length === 0) {
|
|
888
|
+
return null
|
|
889
|
+
}
|
|
806
890
|
return [
|
|
807
|
-
...
|
|
808
|
-
...
|
|
891
|
+
...subGroups,
|
|
892
|
+
...childs
|
|
809
893
|
]
|
|
810
894
|
}
|
|
811
895
|
|
|
@@ -851,12 +935,33 @@ export default class ItemListTree extends Component {
|
|
|
851
935
|
listStyle = {}
|
|
852
936
|
} = this.props
|
|
853
937
|
const level1Bookgroups = ready
|
|
854
|
-
? bookmarkGroups.filter(
|
|
855
|
-
|
|
856
|
-
|
|
938
|
+
? bookmarkGroups.filter(d => {
|
|
939
|
+
if (!d.level || d.level < 2) {
|
|
940
|
+
if (this.state.keyword) {
|
|
941
|
+
const hasMatchedItemsRecursive = (bg) => {
|
|
942
|
+
const ids = bg.bookmarkIds || []
|
|
943
|
+
const subIds = bg.bookmarkGroupIds || []
|
|
944
|
+
const tree = this.props.bookmarksMap
|
|
945
|
+
const { keyword } = this.state
|
|
946
|
+
const hasMatch = ids.some(id => {
|
|
947
|
+
const item = tree.get(id)
|
|
948
|
+
return item && createName(item).toLowerCase().includes(keyword.toLowerCase())
|
|
949
|
+
})
|
|
950
|
+
if (hasMatch) return true
|
|
951
|
+
return subIds.some(sgid => {
|
|
952
|
+
const subBg = window.store.bookmarkGroupTree[sgid]
|
|
953
|
+
return subBg && hasMatchedItemsRecursive(subBg)
|
|
954
|
+
})
|
|
955
|
+
}
|
|
956
|
+
return hasMatchedItemsRecursive(d)
|
|
957
|
+
}
|
|
958
|
+
return true
|
|
959
|
+
}
|
|
960
|
+
return false
|
|
961
|
+
})
|
|
857
962
|
: []
|
|
858
963
|
return (
|
|
859
|
-
<div className={`tree-list item-type-${type}`}>
|
|
964
|
+
<div className={`tree-list item-type-${type}`} ref={this.treeRef}>
|
|
860
965
|
<div className='tree-list-header'>
|
|
861
966
|
{
|
|
862
967
|
staticList
|
|
@@ -3,7 +3,7 @@ import { debounce } from 'lodash-es'
|
|
|
3
3
|
import Search from '../common/search'
|
|
4
4
|
import runIdle from '../../common/run-idle'
|
|
5
5
|
|
|
6
|
-
export default memo(function TreeSearchComponent ({ onSearch, keyword }) {
|
|
6
|
+
export default memo(function TreeSearchComponent ({ onSearch, keyword, autoFocus, onKeyDown }) {
|
|
7
7
|
const [searchTerm, setSearchTerm] = useState(keyword)
|
|
8
8
|
|
|
9
9
|
const performSearch = debounce((term) => {
|
|
@@ -18,11 +18,19 @@ export default memo(function TreeSearchComponent ({ onSearch, keyword }) {
|
|
|
18
18
|
performSearch(term)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
const handleKeyDown = (e) => {
|
|
22
|
+
if (onKeyDown) {
|
|
23
|
+
onKeyDown(e)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
21
27
|
return (
|
|
22
28
|
<Search
|
|
23
29
|
onChange={handleChange}
|
|
30
|
+
onKeyDown={handleKeyDown}
|
|
24
31
|
value={searchTerm}
|
|
25
32
|
allowClear
|
|
33
|
+
autoFocus={autoFocus}
|
|
26
34
|
/>
|
|
27
35
|
)
|
|
28
36
|
})
|
|
@@ -186,6 +186,7 @@ export default class VncSession extends PureComponent {
|
|
|
186
186
|
qualityLevel = 3, // 0-9, lower = faster performance
|
|
187
187
|
compressionLevel = 1, // 0-9, lower = faster performance
|
|
188
188
|
shared = true,
|
|
189
|
+
showDotCursor = true, // show dot cursor when server sends no cursor image (common on Windows)
|
|
189
190
|
username,
|
|
190
191
|
password
|
|
191
192
|
} = tab
|
|
@@ -376,6 +377,7 @@ export default class VncSession extends PureComponent {
|
|
|
376
377
|
rfb.qualityLevel = qualityLevel
|
|
377
378
|
rfb.compressionLevel = compressionLevel
|
|
378
379
|
rfb.viewOnly = viewOnly
|
|
380
|
+
rfb.showDotCursor = showDotCursor
|
|
379
381
|
this.rfb = rfb
|
|
380
382
|
}
|
|
381
383
|
|
|
@@ -49,6 +49,9 @@ export default function WidgetControl ({ formData, widgetInstancesLength }) {
|
|
|
49
49
|
config
|
|
50
50
|
}
|
|
51
51
|
window.store.widgetInstances.push(instance)
|
|
52
|
+
if (config.autoRun) {
|
|
53
|
+
window.store.toggleAutoRunWidget(instance)
|
|
54
|
+
}
|
|
52
55
|
showMsg(msg, 'success', result.serverInfo, 10)
|
|
53
56
|
} catch (err) {
|
|
54
57
|
console.error('Failed to run widget:', err)
|
|
@@ -5,6 +5,7 @@ import React, { useState, useEffect } from 'react'
|
|
|
5
5
|
import { Form, Input, InputNumber, Switch, Select, Button, Tooltip, Alert } from 'antd'
|
|
6
6
|
import { formItemLayout, tailFormItemLayout } from '../../common/form-layout'
|
|
7
7
|
import HelpIcon from '../common/help-icon'
|
|
8
|
+
import BatchOpEditor from '../batch-op/batch-op-editor'
|
|
8
9
|
|
|
9
10
|
export default function WidgetForm ({ widget, onSubmit, loading, hasRunningInstance }) {
|
|
10
11
|
const [form] = Form.useForm()
|
|
@@ -33,6 +34,7 @@ export default function WidgetForm ({ widget, onSubmit, loading, hasRunningInsta
|
|
|
33
34
|
const { info } = widget
|
|
34
35
|
const { configs, type, singleInstance } = info
|
|
35
36
|
const isInstanceWidget = type === 'instance'
|
|
37
|
+
const isFrontendWidget = type === 'frontend'
|
|
36
38
|
const txt = isInstanceWidget ? 'Start widget' : 'Run widget'
|
|
37
39
|
const isDisabled = loading || (singleInstance && hasRunningInstance)
|
|
38
40
|
|
|
@@ -112,6 +114,10 @@ export default function WidgetForm ({ widget, onSubmit, loading, hasRunningInsta
|
|
|
112
114
|
return acc
|
|
113
115
|
}, {})
|
|
114
116
|
|
|
117
|
+
if (isFrontendWidget && info.name === 'Batch Operation') {
|
|
118
|
+
return <BatchOpEditor widget={widget} />
|
|
119
|
+
}
|
|
120
|
+
|
|
115
121
|
return (
|
|
116
122
|
<div className='widget-form'>
|
|
117
123
|
<div className='pd1b alignright'>
|
|
@@ -1,12 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
Popconfirm,
|
|
3
|
+
Popover,
|
|
4
|
+
Tooltip,
|
|
5
|
+
Tag
|
|
6
|
+
} from 'antd'
|
|
7
|
+
import { CloseOutlined, CopyOutlined, ThunderboltOutlined } from '@ant-design/icons'
|
|
3
8
|
import { copy } from '../../common/clipboard'
|
|
9
|
+
import classnames from 'classnames'
|
|
10
|
+
import { auto } from 'manate/react'
|
|
4
11
|
|
|
5
12
|
const e = window.translate
|
|
6
13
|
|
|
7
|
-
export default function WidgetInstance ({ item }) {
|
|
8
|
-
const { id, title, serverInfo } = item
|
|
9
|
-
const cls = 'item-list-unit'
|
|
14
|
+
export default auto(function WidgetInstance ({ item }) {
|
|
15
|
+
const { id, title, serverInfo, autoRun } = item
|
|
16
|
+
const cls = classnames('item-list-unit', {
|
|
17
|
+
'autorun-active': autoRun
|
|
18
|
+
})
|
|
10
19
|
const delProps = {
|
|
11
20
|
title: e('del'),
|
|
12
21
|
className: 'pointer list-item-remove'
|
|
@@ -31,6 +40,9 @@ export default function WidgetInstance ({ item }) {
|
|
|
31
40
|
copy(serverInfo.url)
|
|
32
41
|
}
|
|
33
42
|
}
|
|
43
|
+
const handleToggleAutoRun = () => {
|
|
44
|
+
window.store.toggleAutoRunWidget(item)
|
|
45
|
+
}
|
|
34
46
|
const popoverContent = serverInfo
|
|
35
47
|
? (
|
|
36
48
|
<div>
|
|
@@ -45,12 +57,13 @@ export default function WidgetInstance ({ item }) {
|
|
|
45
57
|
</div>
|
|
46
58
|
)
|
|
47
59
|
: null
|
|
60
|
+
const tag = autoRun ? <Tag color='green'>{e('autoRun')}</Tag> : null
|
|
48
61
|
const titleDiv = (
|
|
49
62
|
<div
|
|
50
63
|
title={title}
|
|
51
64
|
className='elli pd1y pd2x list-item-title'
|
|
52
65
|
>
|
|
53
|
-
{title}
|
|
66
|
+
{tag} {title}
|
|
54
67
|
</div>
|
|
55
68
|
)
|
|
56
69
|
return (
|
|
@@ -71,6 +84,12 @@ export default function WidgetInstance ({ item }) {
|
|
|
71
84
|
)
|
|
72
85
|
: titleDiv
|
|
73
86
|
}
|
|
87
|
+
<Tooltip title='Toggle auto-run'>
|
|
88
|
+
<ThunderboltOutlined
|
|
89
|
+
className='pointer list-item-autorun'
|
|
90
|
+
onClick={handleToggleAutoRun}
|
|
91
|
+
/>
|
|
92
|
+
</Tooltip>
|
|
74
93
|
<Popconfirm
|
|
75
94
|
{...popProps}
|
|
76
95
|
>
|
|
@@ -78,4 +97,4 @@ export default function WidgetInstance ({ item }) {
|
|
|
78
97
|
</Popconfirm>
|
|
79
98
|
</div>
|
|
80
99
|
)
|
|
81
|
-
}
|
|
100
|
+
})
|
package/client/store/common.js
CHANGED
|
@@ -6,7 +6,6 @@ import handleError from '../common/error-handler'
|
|
|
6
6
|
import Modal from '../components/common/modal'
|
|
7
7
|
import { debounce, some, get, pickBy } from 'lodash-es'
|
|
8
8
|
import {
|
|
9
|
-
modals,
|
|
10
9
|
leftSidebarWidthKey,
|
|
11
10
|
rightSidebarWidthKey,
|
|
12
11
|
addPanelWidthLsKey,
|
|
@@ -95,33 +94,6 @@ export default Store => {
|
|
|
95
94
|
window.store['_' + name] = JSON.stringify(value)
|
|
96
95
|
}
|
|
97
96
|
|
|
98
|
-
Store.prototype.toggleBatchOp = function () {
|
|
99
|
-
window.store.showModal = window.store.showModal === modals.batchOps ? modals.hide : modals.batchOps
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
Store.prototype.runBatchOp = function (path) {
|
|
103
|
-
window.store.showModal = modals.batchOps
|
|
104
|
-
async function updateText () {
|
|
105
|
-
const text = await window.fs.readFile(path)
|
|
106
|
-
refsStatic.get('batch-op')?.setState({
|
|
107
|
-
text
|
|
108
|
-
})
|
|
109
|
-
}
|
|
110
|
-
function queue () {
|
|
111
|
-
refsStatic.get('batch-op')?.handleClick()
|
|
112
|
-
}
|
|
113
|
-
function run () {
|
|
114
|
-
refsStatic.get('batch-op')?.handleExec()
|
|
115
|
-
}
|
|
116
|
-
try {
|
|
117
|
-
setTimeout(updateText, 2000)
|
|
118
|
-
setTimeout(queue, 3000)
|
|
119
|
-
setTimeout(run, 4000)
|
|
120
|
-
} catch (e) {
|
|
121
|
-
console.error(e)
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
97
|
Store.prototype.setSettingItem = function (v) {
|
|
126
98
|
window.store.settingItem = v
|
|
127
99
|
}
|
|
@@ -156,7 +156,7 @@ export default () => {
|
|
|
156
156
|
qmSortByFrequency: ls.getItem(qmSortByFrequencyKey) === 'yes',
|
|
157
157
|
|
|
158
158
|
// sidebar
|
|
159
|
-
openedSideBar: ls.getItem(openedSidebarKey),
|
|
159
|
+
openedSideBar: ls.getItem(openedSidebarKey) || '',
|
|
160
160
|
leftSidebarWidth: parseInt(ls.getItem(leftSidebarWidthKey), 10) || 300,
|
|
161
161
|
addPanelWidth: parseInt(ls.getItem(addPanelWidthLsKey), 10) || 300,
|
|
162
162
|
menuOpened: false,
|
|
@@ -201,6 +201,7 @@ export default () => {
|
|
|
201
201
|
// widgets
|
|
202
202
|
widgets: [],
|
|
203
203
|
widgetInstances: [],
|
|
204
|
+
autoRunWidgets: [],
|
|
204
205
|
// move item
|
|
205
206
|
openMoveModal: false,
|
|
206
207
|
moveItem: null,
|
|
@@ -7,12 +7,12 @@ import parseInt10 from '../common/parse-int10'
|
|
|
7
7
|
import { infoTabs, statusMap, defaultEnvLang } from '../common/constants'
|
|
8
8
|
import fs from '../common/fs'
|
|
9
9
|
import generate from '../common/id-with-stamp'
|
|
10
|
+
import { refsStatic } from '../components/common/ref'
|
|
10
11
|
import defaultSettings from '../common/default-setting'
|
|
11
12
|
import encodes from '../components/bookmark-form/common/encodes'
|
|
12
13
|
import { initWsCommon } from '../common/fetch-from-server'
|
|
13
14
|
import safeParse from '../common/parse-json-safe'
|
|
14
15
|
import initWatch from './watch'
|
|
15
|
-
import { refsStatic } from '../components/common/ref'
|
|
16
16
|
import { parseQuickConnect } from '../common/parse-quick-connect'
|
|
17
17
|
|
|
18
18
|
function getHost (argv, opts) {
|
|
@@ -108,8 +108,8 @@ export async function addTabFromCommandLine (store, opts) {
|
|
|
108
108
|
) {
|
|
109
109
|
window.initFolder = options.initFolder
|
|
110
110
|
}
|
|
111
|
-
if (options
|
|
112
|
-
|
|
111
|
+
if (options.batchOp) {
|
|
112
|
+
refsStatic.get('batch-op-runner')?.runBatchOpFromFile(options.batchOp)
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
@@ -208,7 +208,6 @@ export default (Store) => {
|
|
|
208
208
|
)
|
|
209
209
|
setTimeout(
|
|
210
210
|
() => {
|
|
211
|
-
console.log('Auto sync is ready')
|
|
212
211
|
store.autoSyncReady = true
|
|
213
212
|
},
|
|
214
213
|
2000
|
|
@@ -216,6 +215,9 @@ export default (Store) => {
|
|
|
216
215
|
if (store.config.checkUpdateOnStart) {
|
|
217
216
|
store.onCheckUpdate(false)
|
|
218
217
|
}
|
|
218
|
+
store.startAutoRunWidgets().catch(err => {
|
|
219
|
+
console.error('Failed to start autorun widgets:', err)
|
|
220
|
+
})
|
|
219
221
|
}
|
|
220
222
|
Store.prototype.initCommandLine = async function () {
|
|
221
223
|
const opts = await window.pre.runGlobalAsync('initCommandLine')
|
|
@@ -130,6 +130,14 @@ export default Store => {
|
|
|
130
130
|
result = await store.mcpSftpDownload(args)
|
|
131
131
|
break
|
|
132
132
|
|
|
133
|
+
// Transfer list/history operations
|
|
134
|
+
case 'sftp_transfer_list':
|
|
135
|
+
result = store.mcpSftpTransferList()
|
|
136
|
+
break
|
|
137
|
+
case 'sftp_transfer_history':
|
|
138
|
+
result = store.mcpSftpTransferHistory()
|
|
139
|
+
break
|
|
140
|
+
|
|
133
141
|
// Zmodem (trzsz/rzsz) operations
|
|
134
142
|
case 'zmodem_upload':
|
|
135
143
|
result = store.mcpZmodemUpload(args)
|
|
@@ -617,7 +625,7 @@ export default Store => {
|
|
|
617
625
|
throw new Error('remotePath is required')
|
|
618
626
|
}
|
|
619
627
|
|
|
620
|
-
window._transferConflictPolicy = args.conflictPolicy || '
|
|
628
|
+
window._transferConflictPolicy = args.conflictPolicy || 'mergeOrOverwriteAll'
|
|
621
629
|
|
|
622
630
|
const fromFile = await getLocalFileInfo(localPath)
|
|
623
631
|
const transferItem = {
|
|
@@ -662,7 +670,7 @@ export default Store => {
|
|
|
662
670
|
throw new Error('localPath is required')
|
|
663
671
|
}
|
|
664
672
|
|
|
665
|
-
window._transferConflictPolicy = args.conflictPolicy || '
|
|
673
|
+
window._transferConflictPolicy = args.conflictPolicy || 'mergeOrOverwriteAll'
|
|
666
674
|
|
|
667
675
|
const fromFile = await getRemoteFileInfo(sftp, remotePath)
|
|
668
676
|
const transferItem = {
|
|
@@ -692,6 +700,16 @@ export default Store => {
|
|
|
692
700
|
}
|
|
693
701
|
}
|
|
694
702
|
|
|
703
|
+
// ==================== Transfer List/History APIs ====================
|
|
704
|
+
|
|
705
|
+
Store.prototype.mcpSftpTransferList = function () {
|
|
706
|
+
return deepCopy(window.store.fileTransfers)
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
Store.prototype.mcpSftpTransferHistory = function () {
|
|
710
|
+
return deepCopy(window.store.transferHistory)
|
|
711
|
+
}
|
|
712
|
+
|
|
695
713
|
// ==================== Zmodem (trzsz/rzsz) APIs ====================
|
|
696
714
|
|
|
697
715
|
Store.prototype.mcpZmodemUpload = function (args) {
|
package/client/store/sync.js
CHANGED
|
@@ -176,6 +176,30 @@ export default (Store) => {
|
|
|
176
176
|
window.onSyncAll = false
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
Store.prototype.downloadSettingAll = async function () {
|
|
180
|
+
const { store, onSyncAll } = window
|
|
181
|
+
if (store.autoSyncReady === false) {
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
if (onSyncAll) {
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
window.onSyncAll = true
|
|
188
|
+
const types = Object.keys(syncTypes)
|
|
189
|
+
for (const type of types) {
|
|
190
|
+
const gistId = store.getSyncGistId(type)
|
|
191
|
+
if (type === syncTypes.webdav) {
|
|
192
|
+
const serverUrl = get(window.store.config, 'syncSetting.webdavServerUrl')
|
|
193
|
+
if (serverUrl) {
|
|
194
|
+
await store.downloadSetting(type)
|
|
195
|
+
}
|
|
196
|
+
} else if (gistId) {
|
|
197
|
+
await store.downloadSetting(type)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
window.onSyncAll = false
|
|
201
|
+
}
|
|
202
|
+
|
|
179
203
|
Store.prototype.uploadSetting = async function (type) {
|
|
180
204
|
if (window[type + 'IsSyncing']) {
|
|
181
205
|
return false
|
|
@@ -561,7 +585,7 @@ export default (Store) => {
|
|
|
561
585
|
|
|
562
586
|
Store.prototype.handleAutoSync = function (v) {
|
|
563
587
|
const { store } = window
|
|
564
|
-
store.
|
|
588
|
+
store.updateSyncSetting({
|
|
565
589
|
autoSync: v
|
|
566
590
|
})
|
|
567
591
|
}
|
package/client/store/tab.js
CHANGED
|
@@ -509,7 +509,7 @@ export default Store => {
|
|
|
509
509
|
'autoReConnect'
|
|
510
510
|
]
|
|
511
511
|
const { history } = store
|
|
512
|
-
const index = history.findIndex(d => {
|
|
512
|
+
const index = history.filter(d => d.id && d.tab).findIndex(d => {
|
|
513
513
|
for (const key in tab) {
|
|
514
514
|
if (tabPropertiesExcludes.includes(key)) {
|
|
515
515
|
continue
|
package/client/store/watch.js
CHANGED
|
@@ -20,22 +20,6 @@ import { refsStatic } from '../components/common/ref'
|
|
|
20
20
|
import dataCompare from '../common/data-compare'
|
|
21
21
|
|
|
22
22
|
export default store => {
|
|
23
|
-
// autoRun(() => {
|
|
24
|
-
// store.focus()
|
|
25
|
-
// // store.termSearchOpen = false
|
|
26
|
-
// store.termSearchMatchCount = 0
|
|
27
|
-
// return store.activeTabId
|
|
28
|
-
// }).start()
|
|
29
|
-
|
|
30
|
-
// autoRun(() => {
|
|
31
|
-
// if (store.menuOpened) {
|
|
32
|
-
// store.initMenuEvent()
|
|
33
|
-
// } else {
|
|
34
|
-
// store.onCloseMenu()
|
|
35
|
-
// }
|
|
36
|
-
// return store.menuOpened
|
|
37
|
-
// })
|
|
38
|
-
|
|
39
23
|
for (const name of dbNamesForWatch) {
|
|
40
24
|
window[`watch${name}`] = autoRun(async () => {
|
|
41
25
|
if (window.migrating) {
|
|
@@ -68,8 +52,16 @@ export default store => {
|
|
|
68
52
|
)
|
|
69
53
|
}
|
|
70
54
|
await store.updateLastDataUpdateTime()
|
|
71
|
-
if (
|
|
72
|
-
|
|
55
|
+
if (dbNamesForSync.includes(name)) {
|
|
56
|
+
const syncSetting = store.config.syncSetting || {}
|
|
57
|
+
const { autoSync, autoSyncInterval, autoSyncDirection } = syncSetting
|
|
58
|
+
if (autoSync && autoSyncInterval === 0) {
|
|
59
|
+
if (autoSyncDirection === 'download') {
|
|
60
|
+
await store.downloadSettingAll()
|
|
61
|
+
} else {
|
|
62
|
+
await store.uploadSettingAll()
|
|
63
|
+
}
|
|
64
|
+
}
|
|
73
65
|
}
|
|
74
66
|
return store[name]
|
|
75
67
|
})
|
package/client/store/widgets.js
CHANGED
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
settingMap
|
|
8
8
|
} from '../common/constants'
|
|
9
9
|
import getInitItem from '../common/init-setting-item'
|
|
10
|
+
import deepCopy from 'json-deep-copy'
|
|
11
|
+
import generate from '../common/uid'
|
|
10
12
|
|
|
11
13
|
export default Store => {
|
|
12
14
|
Store.prototype.listWidgets = async () => {
|
|
@@ -58,4 +60,56 @@ export default Store => {
|
|
|
58
60
|
store.settingTab = settingMap.widgets
|
|
59
61
|
store.openSettingModal()
|
|
60
62
|
}
|
|
63
|
+
|
|
64
|
+
Store.prototype.toggleAutoRunWidget = (instance) => {
|
|
65
|
+
const { store } = window
|
|
66
|
+
const { widgetId, config } = instance
|
|
67
|
+
if (instance.autoRun) {
|
|
68
|
+
const index = store.autoRunWidgets.findIndex(
|
|
69
|
+
w => w.id === instance.autoRunId
|
|
70
|
+
)
|
|
71
|
+
if (index > -1) {
|
|
72
|
+
store.autoRunWidgets.splice(index, 1)
|
|
73
|
+
}
|
|
74
|
+
instance.autoRun = false
|
|
75
|
+
instance.autoRunId = undefined
|
|
76
|
+
} else {
|
|
77
|
+
const id = generate()
|
|
78
|
+
const item = {
|
|
79
|
+
id,
|
|
80
|
+
widgetId,
|
|
81
|
+
config
|
|
82
|
+
}
|
|
83
|
+
store.autoRunWidgets.push(item)
|
|
84
|
+
instance.autoRun = true
|
|
85
|
+
instance.autoRunId = id
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
Store.prototype.startAutoRunWidgets = async function () {
|
|
90
|
+
const { store } = window
|
|
91
|
+
const items = store.autoRunWidgets
|
|
92
|
+
if (!items || !items.length) {
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
for (const item of items) {
|
|
96
|
+
try {
|
|
97
|
+
const result = await store.runWidget(item.widgetId, deepCopy(item.config))
|
|
98
|
+
if (result && result.instanceId) {
|
|
99
|
+
const instance = {
|
|
100
|
+
id: result.instanceId,
|
|
101
|
+
title: `${result.widgetId} (${result.instanceId})`,
|
|
102
|
+
widgetId: result.widgetId,
|
|
103
|
+
serverInfo: result.serverInfo,
|
|
104
|
+
config: item.config,
|
|
105
|
+
autoRun: true,
|
|
106
|
+
autoRunId: item.id
|
|
107
|
+
}
|
|
108
|
+
store.widgetInstances.push(instance)
|
|
109
|
+
}
|
|
110
|
+
} catch (err) {
|
|
111
|
+
console.error(`Failed to autorun widget ${item.widgetId}:`, err)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
61
115
|
}
|
package/client/views/index.pug
CHANGED
|
@@ -33,8 +33,7 @@ html
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
- if (!isDev)
|
|
36
|
-
link(rel='stylesheet', href='css/' + version + '
|
|
37
|
-
link(rel='stylesheet', href='css/' + version + '-electerm.css')
|
|
36
|
+
link(rel='stylesheet', href='css/style-' + version + '.css')
|
|
38
37
|
style(id='theme-css').
|
|
39
38
|
style(id='custom-css').
|
|
40
39
|
body
|