修复实例页长时间后不自动刷新:回前台强制拉数、SSE假连接兜底、快照请求超时防卡死。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,12 +1,17 @@
|
||||
/**
|
||||
* embed 壳:SSE 收到后台 tick 后拉 JSON 快照更新 DOM,切换 tab 不再重复请求 HTML.
|
||||
* embed 壳:SSE 收到后台 tick 后拉 JSON 快照更新 DOM.
|
||||
* 同时为 standalone/embed 提供:回前台强制刷新、SSE 假连接兜底.
|
||||
*/
|
||||
(function (global) {
|
||||
let liveEventSource = null;
|
||||
let liveReconnectTimer = null;
|
||||
let localLiveVersion = -1;
|
||||
let sseConnected = false;
|
||||
let lastLiveEventAt = 0;
|
||||
let refreshTimer = null;
|
||||
let pendingWhileHidden = false;
|
||||
/** 超过该毫秒无 live 事件则视为断流,允许 interval 兜底轮询 */
|
||||
const LIVE_STALE_MS = 45000;
|
||||
|
||||
function isEmbedShell() {
|
||||
return document.body && document.body.getAttribute("data-embed-shell") === "1";
|
||||
@@ -30,20 +35,37 @@
|
||||
if (tab === "options" && global.OptionsPanelLive && typeof global.OptionsPanelLive.refreshSoft === "function") {
|
||||
global.OptionsPanelLive.refreshSoft(options);
|
||||
}
|
||||
// 数据看板自有 SSE + 快照,不跟 embed live tick 重拉.
|
||||
}
|
||||
|
||||
function scheduleRefresh(opts) {
|
||||
if (document.hidden) {
|
||||
pendingWhileHidden = true;
|
||||
return;
|
||||
}
|
||||
if (refreshTimer) return;
|
||||
const options = opts || {};
|
||||
refreshTimer = setTimeout(function () {
|
||||
refreshTimer = null;
|
||||
if (document.hidden) return;
|
||||
if (document.hidden) {
|
||||
pendingWhileHidden = true;
|
||||
return;
|
||||
}
|
||||
pendingWhileHidden = false;
|
||||
refreshTabData(currentTab(), { silent: true, force: !!options.force });
|
||||
}, 80);
|
||||
}
|
||||
|
||||
function kickRefresh(opts) {
|
||||
pendingWhileHidden = false;
|
||||
if (refreshTimer) {
|
||||
clearTimeout(refreshTimer);
|
||||
refreshTimer = null;
|
||||
}
|
||||
refreshTabData(currentTab(), opts || { silent: true, force: true });
|
||||
}
|
||||
|
||||
function onLiveEvent(data) {
|
||||
lastLiveEventAt = Date.now();
|
||||
const reason = data && data.reason;
|
||||
const ver = Number(data && data.live_version) || 0;
|
||||
if (!ver) return;
|
||||
@@ -59,7 +81,9 @@
|
||||
|
||||
function closeLiveStream() {
|
||||
if (liveEventSource) {
|
||||
liveEventSource.close();
|
||||
try {
|
||||
liveEventSource.close();
|
||||
} catch (_) {}
|
||||
liveEventSource = null;
|
||||
}
|
||||
if (liveReconnectTimer) {
|
||||
@@ -80,6 +104,7 @@
|
||||
});
|
||||
liveEventSource.onopen = function () {
|
||||
sseConnected = true;
|
||||
lastLiveEventAt = Date.now();
|
||||
};
|
||||
liveEventSource.onerror = function () {
|
||||
sseConnected = false;
|
||||
@@ -90,7 +115,23 @@
|
||||
};
|
||||
}
|
||||
|
||||
function isConnectedFresh() {
|
||||
if (!sseConnected || !liveEventSource) return false;
|
||||
if (!lastLiveEventAt) return false;
|
||||
return Date.now() - lastLiveEventAt < LIVE_STALE_MS;
|
||||
}
|
||||
|
||||
function startLive() {
|
||||
document.addEventListener("visibilitychange", function () {
|
||||
if (document.hidden) return;
|
||||
if (isEmbedShell()) {
|
||||
if (!isConnectedFresh()) connectLiveStream();
|
||||
}
|
||||
kickRefresh({ silent: true, force: true });
|
||||
});
|
||||
window.addEventListener("pageshow", function (ev) {
|
||||
if (ev.persisted) kickRefresh({ silent: true, force: true });
|
||||
});
|
||||
if (!isEmbedShell()) return;
|
||||
connectLiveStream();
|
||||
}
|
||||
@@ -98,9 +139,8 @@
|
||||
global.InstanceLive = {
|
||||
start: startLive,
|
||||
refreshTabData: refreshTabData,
|
||||
isConnected: function () {
|
||||
return sseConnected;
|
||||
},
|
||||
kickRefresh: kickRefresh,
|
||||
isConnected: isConnectedFresh,
|
||||
};
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
|
||||
Reference in New Issue
Block a user