@electerm/electerm-react 2.4.16 → 2.4.18
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.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react'
|
|
1
|
+
import React, { useState, useEffect, useRef } from 'react'
|
|
2
2
|
import { createRoot } from 'react-dom/client'
|
|
3
3
|
import {
|
|
4
4
|
CloseOutlined
|
|
@@ -26,15 +26,40 @@ const notify = (messages) => {
|
|
|
26
26
|
let activeMessages = []
|
|
27
27
|
|
|
28
28
|
function MessageItem ({ id, type, content, duration, onRemove, timestamp }) {
|
|
29
|
+
const timeoutIdRef = useRef(null)
|
|
30
|
+
|
|
29
31
|
useEffect(() => {
|
|
30
32
|
if (duration !== 0) {
|
|
31
33
|
const timer = setTimeout(onRemove, duration * 1000)
|
|
32
|
-
|
|
34
|
+
timeoutIdRef.current = timer
|
|
35
|
+
return () => {
|
|
36
|
+
clearTimeout(timeoutIdRef.current)
|
|
37
|
+
timeoutIdRef.current = null
|
|
38
|
+
}
|
|
33
39
|
}
|
|
34
40
|
}, [duration, onRemove, timestamp])
|
|
35
41
|
|
|
42
|
+
const handleMouseEnter = () => {
|
|
43
|
+
if (timeoutIdRef.current) {
|
|
44
|
+
clearTimeout(timeoutIdRef.current)
|
|
45
|
+
timeoutIdRef.current = null
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const handleMouseLeave = () => {
|
|
50
|
+
if (duration !== 0) {
|
|
51
|
+
const timer = setTimeout(onRemove, duration * 1000)
|
|
52
|
+
timeoutIdRef.current = timer
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
36
56
|
return (
|
|
37
|
-
<div
|
|
57
|
+
<div
|
|
58
|
+
className={classnames('message-item', type)}
|
|
59
|
+
id={`message-${id}`}
|
|
60
|
+
onMouseEnter={handleMouseEnter}
|
|
61
|
+
onMouseLeave={handleMouseLeave}
|
|
62
|
+
>
|
|
38
63
|
<div className='message-content-wrap'>
|
|
39
64
|
{messageIcons[type]}
|
|
40
65
|
<div className='message-content'>{content}</div>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react'
|
|
1
|
+
import React, { useState, useEffect, useRef } from 'react'
|
|
2
2
|
import { CloseOutlined } from '@ant-design/icons'
|
|
3
3
|
import classnames from 'classnames'
|
|
4
4
|
import generateId from '../../common/uid'
|
|
@@ -70,17 +70,41 @@ export function NotificationContainer () {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function NotificationItem ({ message, description, type, onClose, duration = 18.5 }) {
|
|
73
|
+
const timeoutRef = useRef(null)
|
|
74
|
+
|
|
73
75
|
useEffect(() => {
|
|
74
76
|
if (duration > 0) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
timeoutRef.current = setTimeout(onClose, duration * 1000)
|
|
78
|
+
}
|
|
79
|
+
return () => {
|
|
80
|
+
if (timeoutRef.current) {
|
|
81
|
+
clearTimeout(timeoutRef.current)
|
|
82
|
+
timeoutRef.current = null
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}, [])
|
|
86
|
+
|
|
87
|
+
const handleMouseEnter = () => {
|
|
88
|
+
if (timeoutRef.current) {
|
|
89
|
+
clearTimeout(timeoutRef.current)
|
|
90
|
+
timeoutRef.current = null
|
|
77
91
|
}
|
|
78
|
-
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const handleMouseLeave = () => {
|
|
95
|
+
if (duration > 0 && !timeoutRef.current) {
|
|
96
|
+
timeoutRef.current = setTimeout(onClose, duration * 1000)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
79
99
|
|
|
80
100
|
const className = classnames('notification', type)
|
|
81
101
|
|
|
82
102
|
return (
|
|
83
|
-
<div
|
|
103
|
+
<div
|
|
104
|
+
className={className}
|
|
105
|
+
onMouseEnter={handleMouseEnter}
|
|
106
|
+
onMouseLeave={handleMouseLeave}
|
|
107
|
+
>
|
|
84
108
|
<div className='notification-content'>
|
|
85
109
|
<div className='notification-message'>
|
|
86
110
|
<div className='notification-icon'>{messageIcons[type]}</div>
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
/* eslint-disable no-template-curly-in-string, no-useless-escape */
|
|
18
|
+
import { runCmd } from './terminal-apis.js'
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Get inline shell integration command for bash (one-liner format)
|
|
@@ -69,6 +70,35 @@ function getFishInlineIntegration () {
|
|
|
69
70
|
].join('; ')
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Get inline shell integration command for sh/ash (one-liner format)
|
|
75
|
+
* Uses PS1 injection as sh/ash lack PROMPT_COMMAND or advanced traps.
|
|
76
|
+
*/
|
|
77
|
+
function getShInlineIntegration () {
|
|
78
|
+
return [
|
|
79
|
+
'if [ -z "$ELECTERM_SHELL_INTEGRATION" ]',
|
|
80
|
+
'then export ELECTERM_SHELL_INTEGRATION=1',
|
|
81
|
+
'__e_esc() { printf "%s" "$1" | sed "s/\\\\/\\\\\\\\/g; s/;/\\\\x3b/g"; }',
|
|
82
|
+
// We wrap the current PS1 with OSC 633 sequences.
|
|
83
|
+
// \033]633;P;Cwd=... \007 marks the directory
|
|
84
|
+
// \033]633;A \007 marks the start of the prompt
|
|
85
|
+
'export PS1="\\e]633;P;Cwd=$(__e_esc "$PWD")\\a\\e]633;A\\a${PS1:-# }"',
|
|
86
|
+
'fi'
|
|
87
|
+
].join('; ')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function detectShellType (shellStr) {
|
|
91
|
+
if (shellStr.includes('bash')) {
|
|
92
|
+
return 'bash'
|
|
93
|
+
} else if (shellStr.includes('zsh')) {
|
|
94
|
+
return 'zsh'
|
|
95
|
+
} else if (shellStr.includes('fish')) {
|
|
96
|
+
return 'fish'
|
|
97
|
+
} else {
|
|
98
|
+
return 'sh'
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
72
102
|
/**
|
|
73
103
|
* Get shell integration command based on detected shell type
|
|
74
104
|
* @param {string} shellType - 'bash', 'zsh', or 'fish'
|
|
@@ -84,7 +114,7 @@ export function getInlineShellIntegration (shellType) {
|
|
|
84
114
|
return getFishInlineIntegration()
|
|
85
115
|
default:
|
|
86
116
|
// Try bash as default for sh-compatible shells
|
|
87
|
-
return
|
|
117
|
+
return getShInlineIntegration()
|
|
88
118
|
}
|
|
89
119
|
}
|
|
90
120
|
|
|
@@ -112,27 +142,23 @@ export function getShellIntegrationCommand (shellType = 'bash') {
|
|
|
112
142
|
const cmd = getInlineShellIntegration(shellType)
|
|
113
143
|
return wrapSilent(cmd, shellType)
|
|
114
144
|
}
|
|
145
|
+
export async function detectRemoteShell (pid) {
|
|
146
|
+
// 1. We try the version variables first.
|
|
147
|
+
// 2. We try your verified fish check: fish --version ...
|
|
148
|
+
// 3. We use ps -p $$ to check the process name (highly reliable in Linux/Docker).
|
|
149
|
+
// This syntax is safe for Bash, Zsh, and Fish.
|
|
150
|
+
const cmd = 'fish --version 2>/dev/null | grep -q fish && echo fish || { env | grep -q ZSH_VERSION && echo zsh || { env | grep -q BASH_VERSION && echo bash || { ps -p $$ -o comm= 2>/dev/null || echo sh; }; }; }'
|
|
115
151
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
export function detectShellType (shellPath = '') {
|
|
122
|
-
if (!shellPath) return 'bash'
|
|
123
|
-
|
|
124
|
-
const normalizedPath = shellPath.toLowerCase()
|
|
152
|
+
const r = await runCmd(pid, cmd)
|
|
153
|
+
.catch((err) => {
|
|
154
|
+
console.error('detectRemoteShell error', err)
|
|
155
|
+
return 'sh'
|
|
156
|
+
})
|
|
125
157
|
|
|
126
|
-
|
|
127
|
-
return 'zsh'
|
|
128
|
-
} else if (normalizedPath.includes('fish')) {
|
|
129
|
-
return 'fish'
|
|
130
|
-
} else if (normalizedPath.includes('bash')) {
|
|
131
|
-
return 'bash'
|
|
132
|
-
} else if (normalizedPath.includes('sh')) {
|
|
133
|
-
// Generic sh, try bash compatibility
|
|
134
|
-
return 'bash'
|
|
135
|
-
}
|
|
158
|
+
const shell = r.trim().toLowerCase()
|
|
136
159
|
|
|
137
|
-
return '
|
|
160
|
+
if (shell.includes('fish')) return 'fish'
|
|
161
|
+
if (shell.includes('zsh')) return 'zsh'
|
|
162
|
+
if (shell.includes('bash')) return 'bash'
|
|
163
|
+
return 'sh' // Fallback for sh/ash/dash
|
|
138
164
|
}
|
|
@@ -52,11 +52,13 @@ import AIIcon from '../icons/ai-icon.jsx'
|
|
|
52
52
|
import { formatBytes } from '../../common/byte-format.js'
|
|
53
53
|
import {
|
|
54
54
|
getShellIntegrationCommand,
|
|
55
|
+
detectRemoteShell,
|
|
55
56
|
detectShellType
|
|
56
57
|
} from './shell.js'
|
|
57
58
|
import * as fs from './fs.js'
|
|
58
59
|
import iconsMap from '../sys-menu/icons-map.jsx'
|
|
59
60
|
import { refs, refsStatic } from '../common/ref.js'
|
|
61
|
+
import ExternalLink from '../common/external-link.jsx'
|
|
60
62
|
import createDefaultLogPath from '../../common/default-log-path.js'
|
|
61
63
|
import SearchResultBar from './terminal-search-bar'
|
|
62
64
|
|
|
@@ -79,6 +81,9 @@ class Term extends Component {
|
|
|
79
81
|
this.id = `term-${this.props.tab.id}`
|
|
80
82
|
refs.add(this.id, this)
|
|
81
83
|
this.currentInput = ''
|
|
84
|
+
this.shellInjected = false
|
|
85
|
+
this.shellType = null
|
|
86
|
+
this.manualCommandHistory = new Set()
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
domRef = createRef()
|
|
@@ -215,6 +220,33 @@ class Term extends Component {
|
|
|
215
220
|
}
|
|
216
221
|
}
|
|
217
222
|
}
|
|
223
|
+
|
|
224
|
+
// Check for shell integration related config changes
|
|
225
|
+
const prevShowSuggestions = prevProps.config.showCmdSuggestions
|
|
226
|
+
const currShowSuggestions = props.config.showCmdSuggestions
|
|
227
|
+
const prevSftpFollow = prevProps.sftpPathFollowSsh
|
|
228
|
+
const currSftpFollow = props.sftpPathFollowSsh
|
|
229
|
+
|
|
230
|
+
if (
|
|
231
|
+
(!prevShowSuggestions && currShowSuggestions) ||
|
|
232
|
+
(!prevSftpFollow && currSftpFollow)
|
|
233
|
+
) {
|
|
234
|
+
// Config was toggled to true, try to inject shell integration if not already done
|
|
235
|
+
if (this.canInjectShellIntegration() && !this.shellInjected) {
|
|
236
|
+
// If there's an active execution queue, add to it
|
|
237
|
+
if (this.executionQueue && this.executionQueue.length > 0) {
|
|
238
|
+
this.executionQueue.unshift({
|
|
239
|
+
type: 'shell_integration',
|
|
240
|
+
execute: async () => {
|
|
241
|
+
await this.injectShellIntegration()
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
} else {
|
|
245
|
+
// No active queue, inject directly
|
|
246
|
+
this.injectShellIntegration()
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
218
250
|
}
|
|
219
251
|
|
|
220
252
|
timers = {}
|
|
@@ -596,7 +628,7 @@ class Term extends Component {
|
|
|
596
628
|
}
|
|
597
629
|
const r = []
|
|
598
630
|
for (const filePath of files) {
|
|
599
|
-
const stat = await getLocalFileInfo(filePath)
|
|
631
|
+
const stat = await getLocalFileInfo(filePath)
|
|
600
632
|
r.push({ ...stat, filePath })
|
|
601
633
|
}
|
|
602
634
|
return r
|
|
@@ -945,6 +977,12 @@ class Term extends Component {
|
|
|
945
977
|
}
|
|
946
978
|
// Handle Enter
|
|
947
979
|
if (d === '\r' || d === '\n') {
|
|
980
|
+
// Add to manual command history if shell integration is not available
|
|
981
|
+
if (this.currentInput.trim() && this.shouldUseManualHistory()) {
|
|
982
|
+
this.manualCommandHistory.add(this.currentInput.trim())
|
|
983
|
+
// Also add to global history for suggestions
|
|
984
|
+
window.store.addCmdHistory(this.currentInput.trim())
|
|
985
|
+
}
|
|
948
986
|
this.currentInput = ''
|
|
949
987
|
return
|
|
950
988
|
}
|
|
@@ -1060,7 +1098,7 @@ class Term extends Component {
|
|
|
1060
1098
|
// })
|
|
1061
1099
|
// }
|
|
1062
1100
|
|
|
1063
|
-
runInitScript = () => {
|
|
1101
|
+
runInitScript = async () => {
|
|
1064
1102
|
window.store.triggerResize()
|
|
1065
1103
|
const {
|
|
1066
1104
|
startDirectory,
|
|
@@ -1072,20 +1110,51 @@ class Term extends Component {
|
|
|
1072
1110
|
if (startFolder) {
|
|
1073
1111
|
scripts.unshift({ script: `cd "${startFolder}"`, delay: 0 })
|
|
1074
1112
|
}
|
|
1075
|
-
this.pendingRunScripts = scripts
|
|
1076
1113
|
|
|
1077
|
-
//
|
|
1078
|
-
|
|
1114
|
+
// Create unified execution queue
|
|
1115
|
+
this.executionQueue = []
|
|
1116
|
+
|
|
1117
|
+
// Add shell integration injection to queue if needed
|
|
1079
1118
|
if (this.canInjectShellIntegration()) {
|
|
1080
|
-
this.
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1119
|
+
this.executionQueue.push({
|
|
1120
|
+
type: 'shell_integration',
|
|
1121
|
+
execute: async () => {
|
|
1122
|
+
await this.injectShellIntegration()
|
|
1123
|
+
}
|
|
1124
|
+
})
|
|
1084
1125
|
}
|
|
1126
|
+
|
|
1127
|
+
// Add delayed scripts to queue
|
|
1128
|
+
scripts.forEach(script => {
|
|
1129
|
+
this.executionQueue.push({
|
|
1130
|
+
type: 'delayed_script',
|
|
1131
|
+
script: script.script,
|
|
1132
|
+
delay: script.delay || 0,
|
|
1133
|
+
execute: () => {
|
|
1134
|
+
if (script.script) {
|
|
1135
|
+
this.attachAddon._sendData(script.script + '\r')
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
})
|
|
1139
|
+
})
|
|
1140
|
+
|
|
1141
|
+
this.processExecutionQueue()
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
shouldUseManualHistory = () => {
|
|
1145
|
+
const useManual = this.props.config.showCmdSuggestions &&
|
|
1146
|
+
(this.shellType === 'sh' || (isWin && this.isLocal()))
|
|
1147
|
+
return useManual
|
|
1085
1148
|
}
|
|
1086
1149
|
|
|
1087
1150
|
canInjectShellIntegration = () => {
|
|
1088
|
-
|
|
1151
|
+
const { config } = this.props
|
|
1152
|
+
const canInject = (config.showCmdSuggestions || this.props.sftpPathFollowSsh) &&
|
|
1153
|
+
(
|
|
1154
|
+
this.isSsh() ||
|
|
1155
|
+
(this.isLocal() && !isWin)
|
|
1156
|
+
)
|
|
1157
|
+
return canInject
|
|
1089
1158
|
}
|
|
1090
1159
|
|
|
1091
1160
|
isSsh = () => {
|
|
@@ -1095,70 +1164,97 @@ class Term extends Component {
|
|
|
1095
1164
|
|
|
1096
1165
|
isLocal = () => {
|
|
1097
1166
|
const { host, type } = this.props.tab
|
|
1098
|
-
return !host &&
|
|
1167
|
+
return !host &&
|
|
1168
|
+
(type === 'local' || type === undefined)
|
|
1099
1169
|
}
|
|
1100
1170
|
|
|
1101
1171
|
/**
|
|
1102
|
-
*
|
|
1103
|
-
* Called after shell integration injection completes (or immediately if disabled)
|
|
1172
|
+
* Process the unified execution queue one item at a time
|
|
1104
1173
|
*/
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
this.delayedScripts = deepCopy(runScripts)
|
|
1109
|
-
this.timers.timerDelay = setTimeout(this.runDelayedScripts, this.delayedScripts[0].delay || 0)
|
|
1174
|
+
processExecutionQueue = async () => {
|
|
1175
|
+
if (!this.executionQueue || this.executionQueue.length === 0) {
|
|
1176
|
+
return
|
|
1110
1177
|
}
|
|
1111
|
-
|
|
1178
|
+
|
|
1179
|
+
const item = this.executionQueue.shift()
|
|
1180
|
+
|
|
1181
|
+
try {
|
|
1182
|
+
if (item.type === 'shell_integration') {
|
|
1183
|
+
await item.execute()
|
|
1184
|
+
} else if (item.type === 'delayed_script') {
|
|
1185
|
+
item.execute()
|
|
1186
|
+
// Wait for the specified delay before processing next item
|
|
1187
|
+
if (item.delay > 0) {
|
|
1188
|
+
await new Promise(resolve => {
|
|
1189
|
+
this.timers.timerDelay = setTimeout(resolve, item.delay)
|
|
1190
|
+
})
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
} catch (error) {
|
|
1194
|
+
console.error('[Shell Integration] Error processing queue item:', item.type, error)
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
// Process next item
|
|
1198
|
+
this.processExecutionQueue()
|
|
1112
1199
|
}
|
|
1113
1200
|
|
|
1114
1201
|
/**
|
|
1115
1202
|
* Inject shell integration commands from client-side
|
|
1116
1203
|
* This replaces the server-side source xxx.xxx approach
|
|
1117
1204
|
* Uses output suppression to hide the injection command
|
|
1205
|
+
* Returns a promise that resolves when injection is complete
|
|
1118
1206
|
*/
|
|
1119
|
-
injectShellIntegration = () => {
|
|
1120
|
-
|
|
1121
|
-
|
|
1207
|
+
injectShellIntegration = async () => {
|
|
1208
|
+
if (this.shellInjected) {
|
|
1209
|
+
return Promise.resolve()
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
let shellType
|
|
1122
1213
|
if (this.isLocal()) {
|
|
1123
1214
|
const { config } = this.props
|
|
1124
1215
|
const localShell = isMac ? config.execMac : config.execLinux
|
|
1125
1216
|
shellType = detectShellType(localShell)
|
|
1217
|
+
} else if (this.isSsh()) {
|
|
1218
|
+
shellType = await detectRemoteShell(this.pid)
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
this.shellType = shellType
|
|
1222
|
+
if (shellType === 'fish') {
|
|
1223
|
+
if (this.props.sftpPathFollowSsh) {
|
|
1224
|
+
message.warning(
|
|
1225
|
+
<span>
|
|
1226
|
+
Fish shell is not supported for SFTP follow SSH path. See: <ExternalLink to='https://github.com/electerm/electerm/wiki/Warning-about-sftp-follow-ssh-path-function'>wiki</ExternalLink>
|
|
1227
|
+
</span>
|
|
1228
|
+
, 7)
|
|
1229
|
+
}
|
|
1230
|
+
return Promise.resolve()
|
|
1126
1231
|
}
|
|
1127
1232
|
|
|
1128
|
-
|
|
1233
|
+
// Don't inject for sh type shells unless sftpPathFollowSsh is true
|
|
1234
|
+
if (shellType === 'sh' && !this.props.sftpPathFollowSsh) {
|
|
1235
|
+
return Promise.resolve()
|
|
1236
|
+
}
|
|
1129
1237
|
|
|
1130
|
-
// Remote sessions might need longer timeout for shell integration detection
|
|
1131
1238
|
const integrationCmd = getShellIntegrationCommand(shellType)
|
|
1132
1239
|
|
|
1133
|
-
|
|
1240
|
+
return new Promise((resolve) => {
|
|
1134
1241
|
// Wait for initial data (prompt/banner) to arrive before injecting
|
|
1135
1242
|
this.attachAddon.onInitialData(() => {
|
|
1136
1243
|
if (this.attachAddon) {
|
|
1137
1244
|
// Start suppressing output before sending the integration command
|
|
1138
1245
|
// This hides the command and its output until OSC 633 is detected
|
|
1139
|
-
const suppressionTimeout =
|
|
1140
|
-
// Pass callback to
|
|
1246
|
+
const suppressionTimeout = this.isSsh() ? 5000 : 3000
|
|
1247
|
+
// Pass callback to resolve the promise after suppression ends
|
|
1141
1248
|
this.attachAddon.startOutputSuppression(suppressionTimeout, () => {
|
|
1142
|
-
this.
|
|
1249
|
+
this.shellInjected = true
|
|
1250
|
+
resolve()
|
|
1143
1251
|
})
|
|
1144
1252
|
this.attachAddon._sendData(integrationCmd)
|
|
1253
|
+
} else {
|
|
1254
|
+
resolve()
|
|
1145
1255
|
}
|
|
1146
1256
|
})
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
runDelayedScripts = () => {
|
|
1151
|
-
const { delayedScripts } = this
|
|
1152
|
-
if (delayedScripts && delayedScripts.length > 0) {
|
|
1153
|
-
const obj = delayedScripts.shift()
|
|
1154
|
-
if (obj.script) {
|
|
1155
|
-
this.attachAddon._sendData(obj.script + '\r')
|
|
1156
|
-
}
|
|
1157
|
-
if (delayedScripts.length > 0) {
|
|
1158
|
-
const nextDelay = delayedScripts[0].delay || 0
|
|
1159
|
-
this.timers.timerDelay = setTimeout(this.runDelayedScripts, nextDelay)
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1257
|
+
})
|
|
1162
1258
|
}
|
|
1163
1259
|
|
|
1164
1260
|
setStatus = status => {
|