@electerm/electerm-react 5.1.25 → 5.3.0
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/build-map.js +12 -0
- package/client/common/constants.js +12 -0
- package/client/common/db.js +7 -5
- package/client/common/import-task.js +174 -0
- package/client/common/term-of-use.jsx +69 -0
- package/client/components/ai/ai-config-props.js +8 -0
- package/client/components/bookmark-form/ai-bookmark-form.jsx +18 -15
- package/client/components/bookmark-form/bookmark-schema.js +1 -0
- package/client/components/bookmark-form/common/bookmark-select.jsx +6 -5
- package/client/components/bookmark-form/common/fields.jsx +1 -1
- package/client/components/bookmark-form/common/profile-item.jsx +12 -2
- package/client/components/bookmark-form/config/common-fields.js +11 -0
- package/client/components/bookmark-form/config/rdp.js +1 -1
- package/client/components/bookmark-form/tree-select.jsx +5 -16
- package/client/components/common/import-progress.jsx +90 -0
- package/client/components/footer/footer-entry.jsx +1 -1
- package/client/components/main/main.jsx +2 -0
- package/client/components/profile/profile-form-rdp.jsx +1 -1
- package/client/components/quick-commands/quick-command-transport-mod.jsx +24 -18
- package/client/components/rdp/rdp-cred-form.jsx +55 -0
- package/client/components/rdp/rdp-session.jsx +97 -6
- package/client/components/setting-panel/keywords-transport.jsx +1 -4
- package/client/components/setting-panel/setting-modal.jsx +8 -2
- package/client/components/setting-panel/start-session-select.jsx +5 -16
- package/client/components/setting-sync/setting-sync.jsx +15 -7
- package/client/components/sidebar/info-modal.jsx +7 -0
- package/client/components/spice/spice-session.jsx +40 -0
- package/client/components/terminal/attach-addon-custom.js +12 -10
- package/client/components/terminal/shell.js +22 -11
- package/client/components/terminal/shortcut-bar-entry.jsx +52 -1
- package/client/components/terminal/terminal-color-query.mjs +32 -27
- package/client/components/terminal/terminal-interactive.jsx +35 -0
- package/client/components/terminal/terminal.jsx +24 -15
- package/client/components/terminal-info/activity.jsx +5 -1
- package/client/components/terminal-info/base.jsx +37 -22
- package/client/components/terminal-info/disk.jsx +5 -1
- package/client/components/terminal-info/local-info-resolver.js +89 -0
- package/client/components/terminal-info/network.jsx +5 -1
- package/client/components/terminal-info/run-cmd.jsx +42 -3
- package/client/components/terminal-info/sys-info.jsx +56 -0
- package/client/components/terminal-info/terminal-info.jsx +4 -1
- package/client/components/terminal-info/terminal-info.styl +22 -7
- package/client/components/tree-list/bookmark-transport.jsx +2 -1
- package/client/components/tree-list/bookmark-upload.js +56 -41
- package/client/components/tree-list/tree-list-row.jsx +11 -4
- package/client/components/tree-list/tree-list-rows.js +6 -3
- package/client/components/tree-list/tree-list.jsx +4 -2
- package/client/store/common.js +38 -3
- package/client/store/load-data.js +6 -1
- package/client/store/setting.js +7 -4
- package/client/store/store.js +3 -12
- package/client/store/sync.js +31 -2
- package/client/store/tab.js +16 -11
- package/client/store/watch.js +10 -1
- package/package.json +1 -1
|
@@ -9,6 +9,7 @@ import TerminalInfoNetwork from './network'
|
|
|
9
9
|
import TerminalInfoResource from './resource'
|
|
10
10
|
import TerminalInfoActivities from './activity'
|
|
11
11
|
import TerminalInfoDisk from './disk'
|
|
12
|
+
import TerminalInfoSys from './sys-info'
|
|
12
13
|
import RunCmd from './run-cmd'
|
|
13
14
|
import { runCmd } from '../terminal/terminal-apis'
|
|
14
15
|
import './terminal-info.styl'
|
|
@@ -21,7 +22,8 @@ export default class TerminalInfoContent extends PureComponent {
|
|
|
21
22
|
swap: {},
|
|
22
23
|
activities: [],
|
|
23
24
|
disks: [],
|
|
24
|
-
network: {}
|
|
25
|
+
network: {},
|
|
26
|
+
sysInfo: null
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
setStateRef = (...args) => {
|
|
@@ -44,6 +46,7 @@ export default class TerminalInfoContent extends PureComponent {
|
|
|
44
46
|
return (
|
|
45
47
|
<>
|
|
46
48
|
<TerminalInfoBase {...props} {...state} />
|
|
49
|
+
<TerminalInfoSys {...props} {...state} />
|
|
47
50
|
<TerminalInfoUp {...props} {...state} />
|
|
48
51
|
<TerminalInfoResource
|
|
49
52
|
{...props} {...state}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
.terminal-info-icon
|
|
3
2
|
color var(--text-dark)
|
|
4
3
|
&:hover
|
|
@@ -23,11 +22,27 @@
|
|
|
23
22
|
display block
|
|
24
23
|
.terminal-info-section
|
|
25
24
|
padding 10px 0
|
|
26
|
-
.terminal-info-
|
|
27
|
-
|
|
28
|
-
.terminal-info-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
.terminal-info-sys
|
|
26
|
+
border-bottom 1px solid var(--main-darker)
|
|
27
|
+
.terminal-info-sys-rows
|
|
28
|
+
display flex
|
|
29
|
+
flex-direction column
|
|
30
|
+
gap 4px
|
|
31
|
+
.terminal-info-sys-row
|
|
32
|
+
display flex
|
|
33
|
+
gap 8px
|
|
34
|
+
align-items baseline
|
|
35
|
+
font-size 12px
|
|
36
|
+
.terminal-info-sys-key
|
|
37
|
+
flex none
|
|
38
|
+
width 80px
|
|
39
|
+
color var(--text-dark)
|
|
40
|
+
text-transform capitalize
|
|
41
|
+
.terminal-info-sys-val
|
|
42
|
+
flex 1
|
|
43
|
+
overflow hidden
|
|
44
|
+
text-overflow ellipsis
|
|
45
|
+
white-space nowrap
|
|
31
46
|
.activity-item
|
|
32
47
|
max-height 1.6em
|
|
33
48
|
line-height 1.6
|
|
@@ -36,4 +51,4 @@
|
|
|
36
51
|
max-height none
|
|
37
52
|
.info-panel-wrap-pin
|
|
38
53
|
.toggle-info-panel-wrap
|
|
39
|
-
color var(--success)
|
|
54
|
+
color var(--success)
|
|
@@ -10,11 +10,12 @@ import {
|
|
|
10
10
|
} from '@ant-design/icons'
|
|
11
11
|
import { Button, Space } from 'antd'
|
|
12
12
|
import Upload from '../common/upload'
|
|
13
|
+
import { beforeBookmarkUpload } from './bookmark-upload'
|
|
13
14
|
|
|
14
15
|
const e = window.translate
|
|
15
16
|
|
|
16
17
|
export default class BookmarkTransport extends PureComponent {
|
|
17
|
-
beforeUpload =
|
|
18
|
+
beforeUpload = beforeBookmarkUpload
|
|
18
19
|
|
|
19
20
|
handleDownload = () => {}
|
|
20
21
|
|
|
@@ -8,7 +8,9 @@ import { action } from 'manate'
|
|
|
8
8
|
import uid from '../../common/uid'
|
|
9
9
|
import time from '../../common/time'
|
|
10
10
|
import { fixBookmarks } from '../../common/db-fix'
|
|
11
|
-
import
|
|
11
|
+
import { runImportTask } from '../../common/import-task'
|
|
12
|
+
|
|
13
|
+
const e = window.translate
|
|
12
14
|
|
|
13
15
|
function fixBookmarksId (bookmarks) {
|
|
14
16
|
return bookmarks.map(item => {
|
|
@@ -21,6 +23,7 @@ function fixBookmarksId (bookmarks) {
|
|
|
21
23
|
return item
|
|
22
24
|
}).filter(Boolean)
|
|
23
25
|
}
|
|
26
|
+
|
|
24
27
|
export const bookmarkUpload = action(async (file) => {
|
|
25
28
|
const { store } = window
|
|
26
29
|
const { bookmarks, bookmarkGroups } = store
|
|
@@ -58,50 +61,62 @@ export const bookmarkUpload = action(async (file) => {
|
|
|
58
61
|
|
|
59
62
|
const fixed = fixBookmarks(bookmarks1)
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
await runImportTask({
|
|
65
|
+
title: e('import'),
|
|
66
|
+
batch: 200,
|
|
67
|
+
stopWatchers: ['bookmarks', 'bookmarkGroups'],
|
|
68
|
+
steps: [
|
|
69
|
+
{
|
|
70
|
+
label: e('bookmarks'),
|
|
71
|
+
items: fixed,
|
|
72
|
+
process: (chunk) => {
|
|
73
|
+
chunk.forEach(bg => {
|
|
74
|
+
if (!bmTree.has(bg.id)) {
|
|
75
|
+
store.bookmarks.push(bg)
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
label: e('bookmarkCategory'),
|
|
82
|
+
items: bookmarkGroups1,
|
|
83
|
+
process: (chunk) => {
|
|
84
|
+
chunk.forEach(bg => {
|
|
85
|
+
if (!bmgTree.has(bg.id)) {
|
|
86
|
+
store.bookmarkGroups.push(bg)
|
|
87
|
+
} else {
|
|
88
|
+
const bg1 = store.bookmarkGroups.find(
|
|
89
|
+
b => b.id === bg.id
|
|
90
|
+
)
|
|
91
|
+
bg1.bookmarkIds = uniq(
|
|
92
|
+
[
|
|
93
|
+
...(bg1.bookmarkIds || []),
|
|
94
|
+
...(bg.bookmarkIds || [])
|
|
95
|
+
]
|
|
96
|
+
)
|
|
97
|
+
bg1.bookmarkGroupIds = uniq(
|
|
98
|
+
[
|
|
99
|
+
...(bg1.bookmarkGroupIds || []),
|
|
100
|
+
...(bg.bookmarkGroupIds || [])
|
|
101
|
+
]
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
label: e('bookmarks'),
|
|
109
|
+
weight: 1,
|
|
110
|
+
run: () => {
|
|
111
|
+
store.fixBookmarkGroups()
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
]
|
|
65
115
|
})
|
|
66
116
|
|
|
67
|
-
bookmarkGroups1.forEach(bg => {
|
|
68
|
-
if (!bmgTree.has(bg.id)) {
|
|
69
|
-
store.bookmarkGroups.push(bg)
|
|
70
|
-
} else {
|
|
71
|
-
const bg1 = store.bookmarkGroups.find(
|
|
72
|
-
b => b.id === bg.id
|
|
73
|
-
)
|
|
74
|
-
bg1.bookmarkIds = uniq(
|
|
75
|
-
[
|
|
76
|
-
...(bg1.bookmarkIds || []),
|
|
77
|
-
...(bg.bookmarkIds || [])
|
|
78
|
-
]
|
|
79
|
-
)
|
|
80
|
-
bg1.bookmarkGroupIds = uniq(
|
|
81
|
-
[
|
|
82
|
-
...(bg1.bookmarkGroupIds || []),
|
|
83
|
-
...(bg.bookmarkGroupIds || [])
|
|
84
|
-
]
|
|
85
|
-
)
|
|
86
|
-
}
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
store.fixBookmarkGroups()
|
|
90
|
-
|
|
91
117
|
return false
|
|
92
118
|
})
|
|
93
119
|
|
|
94
120
|
export async function beforeBookmarkUpload (file) {
|
|
95
|
-
|
|
96
|
-
'bookmarks',
|
|
97
|
-
'bookmarkGroups'
|
|
98
|
-
]
|
|
99
|
-
for (const name of names) {
|
|
100
|
-
window[`watch${name}`].stop()
|
|
101
|
-
}
|
|
102
|
-
bookmarkUpload(file)
|
|
103
|
-
await delay(1000)
|
|
104
|
-
for (const name of names) {
|
|
105
|
-
window[`watch${name}`].start()
|
|
106
|
-
}
|
|
121
|
+
return bookmarkUpload(file)
|
|
107
122
|
}
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
+
import { memo } from 'react'
|
|
1
2
|
import TreeExpander from './tree-expander'
|
|
2
3
|
import TreeListItem from './tree-list-item'
|
|
3
4
|
import TreeItemOp from './tree-item-op'
|
|
4
5
|
import { treeLevelIndent } from './tree-list-layout'
|
|
5
6
|
import createName from '../../common/create-title'
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
// memoized: VirtualTreeList re-renders every scroll frame and re-invokes
|
|
9
|
+
// renderItem for the whole visible window; without memo each row (including
|
|
10
|
+
// the antd Popconfirm/Tooltip in TreeItemOp) re-renders per frame.
|
|
11
|
+
// `expanded` is a boolean (not the expandedKeys array) because the array is
|
|
12
|
+
// mutated in place, so identity-based memo comparison would miss changes.
|
|
13
|
+
function TreeListRow (props) {
|
|
8
14
|
const {
|
|
9
15
|
row,
|
|
10
16
|
keyword,
|
|
11
|
-
|
|
17
|
+
expanded,
|
|
12
18
|
activeItemId,
|
|
13
19
|
searchSelectedRowKey,
|
|
14
20
|
staticList,
|
|
@@ -34,7 +40,6 @@ export default function TreeListRow (props) {
|
|
|
34
40
|
item?.bookmarkIds?.length ||
|
|
35
41
|
item?.bookmarkGroupIds?.length
|
|
36
42
|
)
|
|
37
|
-
const isGroupExpanded = Boolean(keyword) || expandedKeys.includes(item.id)
|
|
38
43
|
const itemProps = {
|
|
39
44
|
item,
|
|
40
45
|
isGroup,
|
|
@@ -94,7 +99,7 @@ export default function TreeListRow (props) {
|
|
|
94
99
|
level={parentId}
|
|
95
100
|
group={item}
|
|
96
101
|
hasChildren={groupHasChildren}
|
|
97
|
-
shouldOpen={
|
|
102
|
+
shouldOpen={expanded}
|
|
98
103
|
onExpand={handleExpand}
|
|
99
104
|
onUnExpand={handleUnExpand}
|
|
100
105
|
/>
|
|
@@ -114,3 +119,5 @@ export default function TreeListRow (props) {
|
|
|
114
119
|
</div>
|
|
115
120
|
)
|
|
116
121
|
}
|
|
122
|
+
|
|
123
|
+
export default memo(TreeListRow)
|
|
@@ -37,7 +37,10 @@ export function buildVisibleTreeRows ({
|
|
|
37
37
|
sortTitle,
|
|
38
38
|
sortHost
|
|
39
39
|
}) {
|
|
40
|
-
const groupTree = bookmarkGroupTree ||
|
|
40
|
+
const groupTree = bookmarkGroupTree || new Map()
|
|
41
|
+
const getGroup = id => groupTree instanceof Map
|
|
42
|
+
? groupTree.get(id)
|
|
43
|
+
: groupTree[id]
|
|
41
44
|
const rows = []
|
|
42
45
|
const matchedRowKeys = []
|
|
43
46
|
const expandedKeySet = new Set(expandedKeys || [])
|
|
@@ -72,7 +75,7 @@ export function buildVisibleTreeRows ({
|
|
|
72
75
|
}
|
|
73
76
|
const hasMatch = (group.bookmarkIds || []).some(bookmarkMatches) ||
|
|
74
77
|
(group.bookmarkGroupIds || []).some(id => {
|
|
75
|
-
return groupHasMatchedBookmarks(
|
|
78
|
+
return groupHasMatchedBookmarks(getGroup(id))
|
|
76
79
|
})
|
|
77
80
|
groupMatchCache.set(group.id, hasMatch)
|
|
78
81
|
return hasMatch
|
|
@@ -100,7 +103,7 @@ export function buildVisibleTreeRows ({
|
|
|
100
103
|
: `#${group.id}`
|
|
101
104
|
|
|
102
105
|
for (const groupId of group.bookmarkGroupIds || []) {
|
|
103
|
-
visitGroup(
|
|
106
|
+
visitGroup(getGroup(groupId), nextParentId, depth + 1)
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
const bookmarkIds = group.bookmarkIds || []
|
|
@@ -740,11 +740,13 @@ export default class ItemListTree extends Component {
|
|
|
740
740
|
}
|
|
741
741
|
|
|
742
742
|
renderVirtualRow = (row, editor) => {
|
|
743
|
+
const { keyword } = this.state
|
|
744
|
+
const { expandedKeys } = this.props
|
|
743
745
|
return (
|
|
744
746
|
<TreeListRow
|
|
745
747
|
row={row}
|
|
746
|
-
keyword={
|
|
747
|
-
|
|
748
|
+
keyword={keyword}
|
|
749
|
+
expanded={Boolean(keyword) || expandedKeys.includes(row.item.id)}
|
|
748
750
|
activeItemId={this.props.activeItemId}
|
|
749
751
|
searchSelectedRowKey={this.state.searchSelectedRowKey}
|
|
750
752
|
staticList={this.props.staticList}
|
package/client/store/common.js
CHANGED
|
@@ -15,10 +15,13 @@ import {
|
|
|
15
15
|
connectionMap,
|
|
16
16
|
lastAiChatSessionIdKey,
|
|
17
17
|
mobileBreakpoint,
|
|
18
|
-
splitMap
|
|
18
|
+
splitMap,
|
|
19
|
+
settingAiId,
|
|
20
|
+
settingSyncId
|
|
19
21
|
} from '../common/constants'
|
|
20
22
|
import * as ls from '../common/safe-local-storage'
|
|
21
23
|
import { refs, refsStatic } from '../components/common/ref'
|
|
24
|
+
import { requireTermOfUse } from '../common/term-of-use'
|
|
22
25
|
import { action } from 'manate'
|
|
23
26
|
import uid from '../common/uid'
|
|
24
27
|
import deepCopy from 'json-deep-copy'
|
|
@@ -56,7 +59,9 @@ export default Store => {
|
|
|
56
59
|
}
|
|
57
60
|
|
|
58
61
|
Store.prototype.toggleAIConfig = function () {
|
|
59
|
-
|
|
62
|
+
requireTermOfUse('ai', () => {
|
|
63
|
+
window.store.showAIConfigModal = true
|
|
64
|
+
})
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
Store.prototype.onResize = debounce(async function () {
|
|
@@ -99,6 +104,18 @@ export default Store => {
|
|
|
99
104
|
}
|
|
100
105
|
|
|
101
106
|
Store.prototype.setSettingItem = function (v) {
|
|
107
|
+
// entering the AI / sync setting page requires the term of use
|
|
108
|
+
// confirmation first (when the term is defined)
|
|
109
|
+
if (
|
|
110
|
+
v && (
|
|
111
|
+
v.id === settingAiId || v.id === settingSyncId
|
|
112
|
+
)
|
|
113
|
+
) {
|
|
114
|
+
const type = v.id === settingAiId ? 'ai' : 'sync'
|
|
115
|
+
return requireTermOfUse(type, () => {
|
|
116
|
+
window.store.settingItem = v
|
|
117
|
+
})
|
|
118
|
+
}
|
|
102
119
|
window.store.settingItem = v
|
|
103
120
|
}
|
|
104
121
|
|
|
@@ -193,7 +210,13 @@ export default Store => {
|
|
|
193
210
|
type,
|
|
194
211
|
authType
|
|
195
212
|
} = tab
|
|
196
|
-
|
|
213
|
+
// SSH picks auth type explicitly (password/privateKey/profiles radio),
|
|
214
|
+
// other types only have the profile dropdown, so a selected profile
|
|
215
|
+
// always means profile auth for them
|
|
216
|
+
const useProfile = type === connectionMap.ssh
|
|
217
|
+
? authType === 'profiles'
|
|
218
|
+
: true
|
|
219
|
+
if (!profile || !useProfile) {
|
|
197
220
|
return tab
|
|
198
221
|
}
|
|
199
222
|
let p = window.store.profiles.find(x => x.id === profile)
|
|
@@ -224,10 +247,17 @@ export default Store => {
|
|
|
224
247
|
...tab,
|
|
225
248
|
...filtered
|
|
226
249
|
}
|
|
250
|
+
} else if (type === connectionMap.ftp) {
|
|
251
|
+
const filtered = pickBy(p.ftp, (value) => value !== undefined && value !== '')
|
|
252
|
+
return {
|
|
253
|
+
...tab,
|
|
254
|
+
...filtered
|
|
255
|
+
}
|
|
227
256
|
}
|
|
228
257
|
delete p.rdp
|
|
229
258
|
delete p.vnc
|
|
230
259
|
delete p.telnet
|
|
260
|
+
delete p.ftp
|
|
231
261
|
const filtered = pickBy(p, (value) => value !== undefined && value !== '')
|
|
232
262
|
return {
|
|
233
263
|
...tab,
|
|
@@ -455,6 +485,11 @@ export default Store => {
|
|
|
455
485
|
let i = len - 1
|
|
456
486
|
for (;i >= 0; i--) {
|
|
457
487
|
const f = profiles[i]
|
|
488
|
+
// migrate old rdp profile key userName -> username
|
|
489
|
+
if (f.rdp?.userName !== undefined) {
|
|
490
|
+
f.rdp.username = f.rdp.userName
|
|
491
|
+
delete f.rdp.userName
|
|
492
|
+
}
|
|
458
493
|
if (f.name) {
|
|
459
494
|
continue
|
|
460
495
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { dbNames, getData, fetchInitData } from '../common/db'
|
|
6
|
+
import deepCopy from 'json-deep-copy'
|
|
6
7
|
import parseInt10 from '../common/parse-int10'
|
|
7
8
|
import { infoTabs, statusMap, defaultEnvLang } from '../common/constants'
|
|
8
9
|
import generate from '../common/id-with-stamp'
|
|
@@ -237,7 +238,11 @@ export default (Store) => {
|
|
|
237
238
|
.then(arr => {
|
|
238
239
|
for (const { name, data } of arr) {
|
|
239
240
|
const dt = JSON.parse(data || '[]')
|
|
240
|
-
|
|
241
|
+
// seed the watcher snapshot with a *copy* - manate wraps the exact
|
|
242
|
+
// array assigned into the store, so seeding with `dt` itself would
|
|
243
|
+
// alias snapshot and store to one backing array; the watcher's
|
|
244
|
+
// no-change early-return would then skip DB writes forever
|
|
245
|
+
refsStatic.add('oldState-' + name, deepCopy(dt))
|
|
241
246
|
if (name === 'bookmarks') {
|
|
242
247
|
ext.bookmarksMap = new Map(
|
|
243
248
|
dt.map(d => [d.id, d])
|
package/client/store/setting.js
CHANGED
|
@@ -14,6 +14,7 @@ import { buildNewTheme } from '../common/terminal-theme'
|
|
|
14
14
|
import getInitItem from '../common/init-setting-item'
|
|
15
15
|
import newTerm from '../common/new-terminal'
|
|
16
16
|
import settingList from '../common/setting-list'
|
|
17
|
+
import { requireTermOfUse } from '../common/term-of-use'
|
|
17
18
|
|
|
18
19
|
const e = window.translate
|
|
19
20
|
|
|
@@ -103,11 +104,13 @@ export default Store => {
|
|
|
103
104
|
) {
|
|
104
105
|
return store.hideSettingModal()
|
|
105
106
|
}
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
requireTermOfUse('sync', () => {
|
|
108
|
+
store.storeAssign({
|
|
109
|
+
settingTab: settingMap.setting
|
|
110
|
+
})
|
|
111
|
+
store.setSettingItem(settingList().find(d => d.id === settingSyncId))
|
|
112
|
+
store.openSettingModal()
|
|
108
113
|
})
|
|
109
|
-
store.setSettingItem(settingList().find(d => d.id === settingSyncId))
|
|
110
|
-
store.openSettingModal()
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
Store.prototype.openTerminalThemes = function () {
|
package/client/store/store.js
CHANGED
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
} from '../common/constants'
|
|
43
43
|
import getInitItem from '../common/init-setting-item'
|
|
44
44
|
import createTitle from '../common/create-title'
|
|
45
|
+
import buildMap from '../common/build-map'
|
|
45
46
|
import {
|
|
46
47
|
theme
|
|
47
48
|
} from 'antd'
|
|
@@ -278,12 +279,7 @@ class Store {
|
|
|
278
279
|
const {
|
|
279
280
|
bookmarks
|
|
280
281
|
} = window.store
|
|
281
|
-
return bookmarks
|
|
282
|
-
return {
|
|
283
|
-
...p,
|
|
284
|
-
[v.id]: v
|
|
285
|
-
}
|
|
286
|
-
}, {})
|
|
282
|
+
return buildMap(bookmarks)
|
|
287
283
|
}
|
|
288
284
|
|
|
289
285
|
hasSshConfig () {
|
|
@@ -296,12 +292,7 @@ class Store {
|
|
|
296
292
|
const {
|
|
297
293
|
bookmarkGroups
|
|
298
294
|
} = window.store
|
|
299
|
-
return bookmarkGroups
|
|
300
|
-
return {
|
|
301
|
-
...p,
|
|
302
|
-
[v.id]: v
|
|
303
|
-
}
|
|
304
|
-
}, {})
|
|
295
|
+
return buildMap(bookmarkGroups)
|
|
305
296
|
}
|
|
306
297
|
|
|
307
298
|
get hasOldConnectionHoppingBookmark () {
|
package/client/store/sync.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { get, pick, debounce } from 'lodash-es'
|
|
6
6
|
import copy from 'json-deep-copy'
|
|
7
|
+
import { action } from 'manate'
|
|
7
8
|
import {
|
|
8
9
|
settingMap, packInfo, syncTypes, syncDataMaps
|
|
9
10
|
} from '../common/constants'
|
|
@@ -13,6 +14,9 @@ import download from '../common/download'
|
|
|
13
14
|
import { fixBookmarks } from '../common/db-fix'
|
|
14
15
|
import dayjs from 'dayjs'
|
|
15
16
|
import parseJsonSafe from '../common/parse-json-safe'
|
|
17
|
+
import { runImportTask } from '../common/import-task'
|
|
18
|
+
|
|
19
|
+
const e = window.translate
|
|
16
20
|
|
|
17
21
|
const {
|
|
18
22
|
version: packVer
|
|
@@ -694,15 +698,40 @@ export default (Store) => {
|
|
|
694
698
|
const { store } = window
|
|
695
699
|
const objs = JSON.parse(txt)
|
|
696
700
|
const { names } = store.getDataSyncNames(true)
|
|
701
|
+
const fixed = {}
|
|
697
702
|
for (const n of names) {
|
|
698
|
-
let arr = objs[n]
|
|
703
|
+
let arr = objs[n] || []
|
|
699
704
|
if (n === settingMap.terminalThemes) {
|
|
700
705
|
arr = store.fixThemes(arr)
|
|
701
706
|
} else if (n === settingMap.bookmarks) {
|
|
702
707
|
arr = fixBookmarks(arr)
|
|
703
708
|
}
|
|
704
|
-
|
|
709
|
+
fixed[n] = arr
|
|
705
710
|
}
|
|
711
|
+
// clear all targets first - importAll replaces data sets, not appends.
|
|
712
|
+
// watchers are stopped inside runImportTask, so this is silent until restart
|
|
713
|
+
action(() => {
|
|
714
|
+
for (const n of names) {
|
|
715
|
+
store.setItems(n, [])
|
|
716
|
+
}
|
|
717
|
+
})()
|
|
718
|
+
await runImportTask({
|
|
719
|
+
title: e('import'),
|
|
720
|
+
batch: 200,
|
|
721
|
+
stopWatchers: names,
|
|
722
|
+
// one chunked step per data set (bookmarks, groups, themes, ...),
|
|
723
|
+
// so the progress bar advances per batch instead of per data set
|
|
724
|
+
steps: names.map((n) => {
|
|
725
|
+
const arr = fixed[n]
|
|
726
|
+
return {
|
|
727
|
+
label: n,
|
|
728
|
+
items: arr,
|
|
729
|
+
process: (chunk) => {
|
|
730
|
+
store[n].push(...chunk)
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
})
|
|
734
|
+
})
|
|
706
735
|
store.updateConfig(stripServerManagedKeys(objs.config))
|
|
707
736
|
if (objs.config?.theme) {
|
|
708
737
|
store.setTheme(objs.config.theme)
|
package/client/store/tab.js
CHANGED
|
@@ -111,17 +111,22 @@ export default Store => {
|
|
|
111
111
|
// Remove old tab
|
|
112
112
|
tabs.splice(index, 1)
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
114
|
+
// Update active tab id synchronously (same as addTab) so the new tab's
|
|
115
|
+
// container is already visible (display:block) by the time its Terminal
|
|
116
|
+
// mounts. Deferring this via setTimeout(0) left the container hidden at
|
|
117
|
+
// mount time, causing the initial fit()/createTerm() to use xterm's
|
|
118
|
+
// default 80x24 size instead of the real fitted size - which then got
|
|
119
|
+
// baked into the remote pty and only partially corrected later, leaving
|
|
120
|
+
// full-screen apps like tmux/vim rendering at the wrong size.
|
|
121
|
+
if (store.activeTabId === tabId) {
|
|
122
|
+
store.activeTabId = newTab.id
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Update batch current tab ID if needed
|
|
126
|
+
const batchProp = `activeTabId${oldBatch}`
|
|
127
|
+
if (store[batchProp] === tabId) {
|
|
128
|
+
store[batchProp] = newTab.id
|
|
129
|
+
}
|
|
125
130
|
}
|
|
126
131
|
|
|
127
132
|
Store.prototype.reloadAllTabs = function () {
|
package/client/store/watch.js
CHANGED
|
@@ -31,6 +31,16 @@ export default store => {
|
|
|
31
31
|
old,
|
|
32
32
|
n
|
|
33
33
|
)
|
|
34
|
+
const newOrder = (n || []).map(d => d.id)
|
|
35
|
+
// with large collections (thousands of bookmarks) this autoRun fires
|
|
36
|
+
// often; when neither content nor order changed, skip the deep-copy
|
|
37
|
+
// snapshot and the DB writes entirely
|
|
38
|
+
const orderChanged = !old ||
|
|
39
|
+
old.length !== (n || []).length ||
|
|
40
|
+
old.some((d, i) => d.id !== newOrder[i])
|
|
41
|
+
if (!updated.length && !added.length && !removed.length && !orderChanged) {
|
|
42
|
+
return store[name]
|
|
43
|
+
}
|
|
34
44
|
// Update snapshot immediately before async DB writes to prevent
|
|
35
45
|
// race conditions: a second autoRun firing before the first
|
|
36
46
|
// completes would see stale oldState and re-insert the same items,
|
|
@@ -41,7 +51,6 @@ export default store => {
|
|
|
41
51
|
...updated.map(item => update(item.id, item, name, false)),
|
|
42
52
|
added.length ? insert(name, added) : Promise.resolve()
|
|
43
53
|
])
|
|
44
|
-
const newOrder = (n || []).map(d => d.id)
|
|
45
54
|
await update(
|
|
46
55
|
`${name}:order`,
|
|
47
56
|
newOrder
|