@electerm/electerm-react 3.15.46 → 3.15.50
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/import-retry.js +23 -0
- package/client/components/ai/ai-chat-entry.jsx +2 -1
- package/client/components/rdp/rdp-session.jsx +2 -1
- package/client/components/setting-panel/setting-modal.jsx +7 -6
- package/client/components/spice/spice-session.jsx +2 -1
- package/client/components/terminal/xterm-loader.js +13 -11
- package/client/components/terminal-info/terminal-info-entry.jsx +2 -1
- package/client/components/text-editor/text-editor-entry.jsx +2 -1
- package/client/components/vnc/vnc-session.jsx +2 -1
- package/client/store/watch.js +31 -37
- package/package.json +1 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry wrapper for dynamic import() calls
|
|
3
|
+
* Handles transient "Failed to fetch" errors that can occur
|
|
4
|
+
* when the app starts and chunks are fetched before the
|
|
5
|
+
* network/server is fully ready
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const MAX_RETRIES = 3
|
|
9
|
+
const RETRY_DELAY = 500
|
|
10
|
+
|
|
11
|
+
function sleep (ms) {
|
|
12
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default function importRetry (factory, retries = MAX_RETRIES) {
|
|
16
|
+
return factory().catch(async (err) => {
|
|
17
|
+
if (retries <= 0) {
|
|
18
|
+
throw err
|
|
19
|
+
}
|
|
20
|
+
await sleep(RETRY_DELAY)
|
|
21
|
+
return importRetry(factory, retries - 1)
|
|
22
|
+
})
|
|
23
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { lazy, Suspense } from 'react'
|
|
2
|
+
import importRetry from '../../common/import-retry'
|
|
2
3
|
|
|
3
|
-
const AIChat = lazy(() => import('./ai-chat'))
|
|
4
|
+
const AIChat = lazy(() => importRetry(() => import('./ai-chat')))
|
|
4
5
|
|
|
5
6
|
export default function AIChatEntry (props) {
|
|
6
7
|
return (
|
|
@@ -28,6 +28,7 @@ import { FileTransferManager, createFileLogger } from './file-transfer'
|
|
|
28
28
|
import { notification } from '../common/notification'
|
|
29
29
|
import message from '../common/message'
|
|
30
30
|
import ShowItem from '../common/show-item'
|
|
31
|
+
import importRetry from '../../common/import-retry'
|
|
31
32
|
import './rdp.styl'
|
|
32
33
|
|
|
33
34
|
const { Option } = Select
|
|
@@ -35,7 +36,7 @@ const { Option } = Select
|
|
|
35
36
|
async function loadWasmModule () {
|
|
36
37
|
if (window.ironRdp) return
|
|
37
38
|
console.debug('[RDP-CLIENT] Loading IronRDP WASM module...')
|
|
38
|
-
const mod = await import('ironrdp-wasm')
|
|
39
|
+
const mod = await importRetry(() => import('ironrdp-wasm'))
|
|
39
40
|
window.ironRdp = {
|
|
40
41
|
wasmInit: mod.default,
|
|
41
42
|
wasmSetup: mod.setup,
|
|
@@ -11,13 +11,14 @@ import {
|
|
|
11
11
|
settingMap,
|
|
12
12
|
modals
|
|
13
13
|
} from '../../common/constants'
|
|
14
|
+
import importRetry from '../../common/import-retry'
|
|
14
15
|
|
|
15
|
-
const TabBookmarks = lazy(() => import('./tab-bookmarks'))
|
|
16
|
-
const TabQuickCommands = lazy(() => import('./tab-quick-commands'))
|
|
17
|
-
const TabSettings = lazy(() => import('./tab-settings'))
|
|
18
|
-
const TabThemes = lazy(() => import('./tab-themes'))
|
|
19
|
-
const TabProfiles = lazy(() => import('./tab-profiles'))
|
|
20
|
-
const TabWidgets = lazy(() => import('./tab-widgets'))
|
|
16
|
+
const TabBookmarks = lazy(() => importRetry(() => import('./tab-bookmarks')))
|
|
17
|
+
const TabQuickCommands = lazy(() => importRetry(() => import('./tab-quick-commands')))
|
|
18
|
+
const TabSettings = lazy(() => importRetry(() => import('./tab-settings')))
|
|
19
|
+
const TabThemes = lazy(() => importRetry(() => import('./tab-themes')))
|
|
20
|
+
const TabProfiles = lazy(() => importRetry(() => import('./tab-profiles')))
|
|
21
|
+
const TabWidgets = lazy(() => importRetry(() => import('./tab-widgets')))
|
|
21
22
|
|
|
22
23
|
const Loading = () => <div style={{ padding: 20, textAlign: 'center' }}><Spin /></div>
|
|
23
24
|
|
|
@@ -15,11 +15,12 @@ import {
|
|
|
15
15
|
} from 'antd'
|
|
16
16
|
import * as ls from '../../common/safe-local-storage'
|
|
17
17
|
import RemoteFloatControl from '../common/remote-float-control'
|
|
18
|
+
import importRetry from '../../common/import-retry'
|
|
18
19
|
import './spice.styl'
|
|
19
20
|
|
|
20
21
|
async function loadSpiceModule () {
|
|
21
22
|
if (window.spiceHtml5) return
|
|
22
|
-
const mod = await import('spice-client')
|
|
23
|
+
const mod = await importRetry(() => import('spice-client'))
|
|
23
24
|
window.spiceHtml5 = {
|
|
24
25
|
SpiceMainConn: mod.SpiceMainConn,
|
|
25
26
|
sendCtrlAltDel: mod.sendCtrlAltDel
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import importRetry from '../../common/import-retry'
|
|
2
|
+
|
|
1
3
|
window.xtermAddons = window.xtermAddons || {}
|
|
2
4
|
|
|
3
5
|
let xtermCssLoaded = false
|
|
@@ -5,76 +7,76 @@ let xtermCssLoaded = false
|
|
|
5
7
|
function loadXtermCss () {
|
|
6
8
|
if (xtermCssLoaded) return
|
|
7
9
|
xtermCssLoaded = true
|
|
8
|
-
import('@xterm/xterm/css/xterm.css')
|
|
10
|
+
importRetry(() => import('@xterm/xterm/css/xterm.css'))
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
export async function loadTerminal () {
|
|
12
14
|
if (window.xtermAddons.Terminal) return window.xtermAddons.Terminal
|
|
13
15
|
loadXtermCss()
|
|
14
|
-
const mod = await import('@xterm/xterm')
|
|
16
|
+
const mod = await importRetry(() => import('@xterm/xterm'))
|
|
15
17
|
window.xtermAddons.Terminal = mod.Terminal
|
|
16
18
|
return window.xtermAddons.Terminal
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export async function loadFitAddon () {
|
|
20
22
|
if (window.xtermAddons.FitAddon) return window.xtermAddons.FitAddon
|
|
21
|
-
const mod = await import('@xterm/addon-fit')
|
|
23
|
+
const mod = await importRetry(() => import('@xterm/addon-fit'))
|
|
22
24
|
window.xtermAddons.FitAddon = mod.FitAddon
|
|
23
25
|
return window.xtermAddons.FitAddon
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
export async function loadAttachAddon () {
|
|
27
29
|
if (window.xtermAddons.AttachAddon) return window.xtermAddons.AttachAddon
|
|
28
|
-
const mod = await import('@xterm/addon-attach')
|
|
30
|
+
const mod = await importRetry(() => import('@xterm/addon-attach'))
|
|
29
31
|
window.xtermAddons.AttachAddon = mod.AttachAddon
|
|
30
32
|
return window.xtermAddons.AttachAddon
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
export async function loadWebLinksAddon () {
|
|
34
36
|
if (window.xtermAddons.WebLinksAddon) return window.xtermAddons.WebLinksAddon
|
|
35
|
-
const mod = await import('@xterm/addon-web-links')
|
|
37
|
+
const mod = await importRetry(() => import('@xterm/addon-web-links'))
|
|
36
38
|
window.xtermAddons.WebLinksAddon = mod.WebLinksAddon
|
|
37
39
|
return window.xtermAddons.WebLinksAddon
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
export async function loadCanvasAddon () {
|
|
41
43
|
if (window.xtermAddons.CanvasAddon) return window.xtermAddons.CanvasAddon
|
|
42
|
-
const mod = await import('@xterm/addon-canvas')
|
|
44
|
+
const mod = await importRetry(() => import('@xterm/addon-canvas'))
|
|
43
45
|
window.xtermAddons.CanvasAddon = mod.CanvasAddon
|
|
44
46
|
return window.xtermAddons.CanvasAddon
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
export async function loadWebglAddon () {
|
|
48
50
|
if (window.xtermAddons.WebglAddon) return window.xtermAddons.WebglAddon
|
|
49
|
-
const mod = await import('@xterm/addon-webgl')
|
|
51
|
+
const mod = await importRetry(() => import('@xterm/addon-webgl'))
|
|
50
52
|
window.xtermAddons.WebglAddon = mod.WebglAddon
|
|
51
53
|
return window.xtermAddons.WebglAddon
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
export async function loadSearchAddon () {
|
|
55
57
|
if (window.xtermAddons.SearchAddon) return window.xtermAddons.SearchAddon
|
|
56
|
-
const mod = await import('@xterm/addon-search')
|
|
58
|
+
const mod = await importRetry(() => import('@xterm/addon-search'))
|
|
57
59
|
window.xtermAddons.SearchAddon = mod.SearchAddon
|
|
58
60
|
return window.xtermAddons.SearchAddon
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
export async function loadLigaturesAddon () {
|
|
62
64
|
if (window.xtermAddons.LigaturesAddon) return window.xtermAddons.LigaturesAddon
|
|
63
|
-
const mod = await import('@xterm/addon-ligatures')
|
|
65
|
+
const mod = await importRetry(() => import('@xterm/addon-ligatures'))
|
|
64
66
|
window.xtermAddons.LigaturesAddon = mod.LigaturesAddon
|
|
65
67
|
return window.xtermAddons.LigaturesAddon
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
export async function loadUnicode11Addon () {
|
|
69
71
|
if (window.xtermAddons.Unicode11Addon) return window.xtermAddons.Unicode11Addon
|
|
70
|
-
const mod = await import('@xterm/addon-unicode11')
|
|
72
|
+
const mod = await importRetry(() => import('@xterm/addon-unicode11'))
|
|
71
73
|
window.xtermAddons.Unicode11Addon = mod.Unicode11Addon
|
|
72
74
|
return window.xtermAddons.Unicode11Addon
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
export async function loadImageAddon () {
|
|
76
78
|
if (window.xtermAddons.ImageAddon) return window.xtermAddons.ImageAddon
|
|
77
|
-
const mod = await import('@xterm/addon-image')
|
|
79
|
+
const mod = await importRetry(() => import('@xterm/addon-image'))
|
|
78
80
|
window.xtermAddons.ImageAddon = mod.ImageAddon
|
|
79
81
|
return window.xtermAddons.ImageAddon
|
|
80
82
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { lazy, Suspense } from 'react'
|
|
2
|
+
import importRetry from '../../common/import-retry'
|
|
2
3
|
|
|
3
|
-
const TerminalInfo = lazy(() => import('./terminal-info'))
|
|
4
|
+
const TerminalInfo = lazy(() => importRetry(() => import('./terminal-info')))
|
|
4
5
|
|
|
5
6
|
export default function TerminalInfoEntry (props) {
|
|
6
7
|
return (
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { lazy, Suspense } from 'react'
|
|
2
|
+
import importRetry from '../../common/import-retry'
|
|
2
3
|
|
|
3
|
-
const TextEditor = lazy(() => import('./text-editor'))
|
|
4
|
+
const TextEditor = lazy(() => importRetry(() => import('./text-editor')))
|
|
4
5
|
|
|
5
6
|
export default function TextEditorEntry (props) {
|
|
6
7
|
return (
|
|
@@ -18,13 +18,14 @@ import Modal from '../common/modal'
|
|
|
18
18
|
import { copy } from '../../common/clipboard'
|
|
19
19
|
import VncForm from './vnc-form'
|
|
20
20
|
import RemoteFloatControl from '../common/remote-float-control'
|
|
21
|
+
import importRetry from '../../common/import-retry'
|
|
21
22
|
import './vnc.styl'
|
|
22
23
|
|
|
23
24
|
// noVNC module imports — loaded dynamically
|
|
24
25
|
async function loadVncModule () {
|
|
25
26
|
if (window.novnc) return
|
|
26
27
|
console.debug('[VNC-CLIENT] Loading noVNC module...')
|
|
27
|
-
const mod = await import('@novnc/novnc/core/rfb')
|
|
28
|
+
const mod = await importRetry(() => import('@novnc/novnc/core/rfb'))
|
|
28
29
|
window.novnc = {
|
|
29
30
|
RFB: mod.default
|
|
30
31
|
}
|
package/client/store/watch.js
CHANGED
|
@@ -21,51 +21,45 @@ import dataCompare from '../common/data-compare'
|
|
|
21
21
|
|
|
22
22
|
export default store => {
|
|
23
23
|
for (const name of dbNamesForWatch) {
|
|
24
|
-
window[`watch${name}Running`] = false
|
|
25
24
|
window[`watch${name}`] = autoRun(async () => {
|
|
26
25
|
const n = store.getItems(name)
|
|
27
|
-
if (window.migrating
|
|
26
|
+
if (window.migrating) {
|
|
28
27
|
return
|
|
29
28
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
const old = refsStatic.get('oldState-' + name)
|
|
30
|
+
const { updated, added, removed } = dataCompare(
|
|
31
|
+
old,
|
|
32
|
+
n
|
|
33
|
+
)
|
|
34
|
+
await Promise.all([
|
|
35
|
+
...removed.map(item => remove(name, item.id)),
|
|
36
|
+
...updated.map(item => update(item.id, item, name, false)),
|
|
37
|
+
added.length ? insert(name, added) : Promise.resolve()
|
|
38
|
+
])
|
|
39
|
+
const newOrder = (n || []).map(d => d.id)
|
|
40
|
+
await update(
|
|
41
|
+
`${name}:order`,
|
|
42
|
+
newOrder
|
|
43
|
+
)
|
|
44
|
+
refsStatic.add('oldState-' + name, deepCopy(n) || [])
|
|
45
|
+
if (name === 'bookmarks') {
|
|
46
|
+
store.bookmarksMap = new Map(
|
|
47
|
+
n.map(d => [d.id, d])
|
|
36
48
|
)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
refsStatic.add('oldState-' + name, deepCopy(n) || [])
|
|
48
|
-
if (name === 'bookmarks') {
|
|
49
|
-
store.bookmarksMap = new Map(
|
|
50
|
-
n.map(d => [d.id, d])
|
|
51
|
-
)
|
|
52
|
-
}
|
|
53
|
-
await store.updateLastDataUpdateTime()
|
|
54
|
-
if (dbNamesForSync.includes(name)) {
|
|
55
|
-
const syncSetting = store.config.syncSetting || {}
|
|
56
|
-
const { autoSync, autoSyncInterval, autoSyncDirection } = syncSetting
|
|
57
|
-
if (autoSync && autoSyncInterval === 0) {
|
|
58
|
-
if (autoSyncDirection === 'download') {
|
|
59
|
-
await store.downloadSettingAll()
|
|
60
|
-
} else {
|
|
61
|
-
await store.uploadSettingAll()
|
|
62
|
-
}
|
|
49
|
+
}
|
|
50
|
+
await store.updateLastDataUpdateTime()
|
|
51
|
+
if (dbNamesForSync.includes(name)) {
|
|
52
|
+
const syncSetting = store.config.syncSetting || {}
|
|
53
|
+
const { autoSync, autoSyncInterval, autoSyncDirection } = syncSetting
|
|
54
|
+
if (autoSync && autoSyncInterval === 0) {
|
|
55
|
+
if (autoSyncDirection === 'download') {
|
|
56
|
+
await store.downloadSettingAll()
|
|
57
|
+
} else {
|
|
58
|
+
await store.uploadSettingAll()
|
|
63
59
|
}
|
|
64
60
|
}
|
|
65
|
-
return store[name]
|
|
66
|
-
} finally {
|
|
67
|
-
window[`watch${name}Running`] = false
|
|
68
61
|
}
|
|
62
|
+
return store[name]
|
|
69
63
|
})
|
|
70
64
|
window[`watch${name}`].start()
|
|
71
65
|
}
|