fix: enable threaded Flask for SSE and make embed tab switch instant
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -9420,4 +9420,4 @@ _purge_key_monitors_if_full_margin()
|
||||
# 启动
|
||||
if __name__ == "__main__":
|
||||
threading.Thread(target=background_task, daemon=True).start()
|
||||
app.run(host=HOST, port=PORT, debug=DEBUG)
|
||||
app.run(host=HOST, port=PORT, debug=DEBUG, threaded=True)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* 中控 iframe 壳:顶栏/统计常驻,tab 内容走 /api/embed/page/<tab>。
|
||||
* 各 tab 面板常驻 DOM,切换时 show/hide,避免整页替换闪屏。
|
||||
* 各 tab 面板常驻 DOM,切换时 show/hide;脚本延后到首次激活,回访零请求。
|
||||
*/
|
||||
(function (global) {
|
||||
const TAB_PATH = {
|
||||
@@ -17,6 +17,7 @@
|
||||
let navToken = 0;
|
||||
let loadingTab = false;
|
||||
const tabPanes = new Map();
|
||||
const tabBooted = new Set();
|
||||
|
||||
/** 自带校验后 form.submit() 的表单,勿在捕获阶段再 fetch 一份(会双发 POST) */
|
||||
const CUSTOM_SUBMIT_FORM_IDS = new Set(["add-order-form", "key-form", "roll-form"]);
|
||||
@@ -60,11 +61,17 @@
|
||||
else history.pushState({ embedTab: tab }, "", url);
|
||||
}
|
||||
|
||||
function notifyParentTabSwitch(tab) {
|
||||
try {
|
||||
window.parent.postMessage({ type: "instance-frame-navigating", embedShellTab: true, tab: tab }, "*");
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
function runPageInit(tab, opts) {
|
||||
const options = opts || {};
|
||||
const revisit = !!options.revisit;
|
||||
document.body.setAttribute("data-page", tab);
|
||||
if (typeof global.attachListWindowToExports === "function") {
|
||||
if (!revisit && typeof global.attachListWindowToExports === "function") {
|
||||
global.attachListWindowToExports();
|
||||
}
|
||||
if (tab === "trade") {
|
||||
@@ -91,9 +98,6 @@
|
||||
if (!revisit && tab === "stats") {
|
||||
if (typeof global.initStatsSegmentFromUrl === "function") global.initStatsSegmentFromUrl();
|
||||
}
|
||||
if (revisit && tab === "options" && global.OptionsPanelLive && typeof global.OptionsPanelLive.refreshSoft === "function") {
|
||||
global.OptionsPanelLive.refreshSoft();
|
||||
}
|
||||
if (!revisit) {
|
||||
if (typeof global.refreshPriceSnapshotConditional === "function") {
|
||||
global.refreshPriceSnapshotConditional();
|
||||
@@ -120,10 +124,20 @@
|
||||
|
||||
function showPane(tab) {
|
||||
tabPanes.forEach((pane, name) => {
|
||||
pane.hidden = name !== tab;
|
||||
const on = name === tab;
|
||||
pane.hidden = !on;
|
||||
pane.classList.toggle("is-active-pane", on);
|
||||
});
|
||||
}
|
||||
|
||||
function bootPaneScripts(tab) {
|
||||
if (tabBooted.has(tab)) return;
|
||||
const pane = tabPanes.get(tab);
|
||||
if (!pane) return;
|
||||
runScripts(pane);
|
||||
tabBooted.add(tab);
|
||||
}
|
||||
|
||||
function mountPane(tab, html) {
|
||||
const root = pageRoot();
|
||||
if (!root) return null;
|
||||
@@ -137,7 +151,6 @@
|
||||
|
||||
const holder = document.createElement("div");
|
||||
holder.innerHTML = html;
|
||||
runScripts(holder);
|
||||
while (holder.firstChild) pane.appendChild(holder.firstChild);
|
||||
|
||||
root.appendChild(pane);
|
||||
@@ -153,11 +166,12 @@
|
||||
if (!root.childNodes.length) return;
|
||||
|
||||
const pane = document.createElement("div");
|
||||
pane.className = "embed-tab-pane";
|
||||
pane.className = "embed-tab-pane is-active-pane";
|
||||
pane.setAttribute("data-embed-pane", tab);
|
||||
Array.from(root.childNodes).forEach((node) => pane.appendChild(node));
|
||||
root.appendChild(pane);
|
||||
tabPanes.set(tab, pane);
|
||||
tabBooted.add(tab);
|
||||
showPane(tab);
|
||||
}
|
||||
|
||||
@@ -189,17 +203,56 @@
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function preloadAllTabs() {
|
||||
const tabs = Object.keys(TAB_PATH);
|
||||
const current = getTab();
|
||||
let idx = 0;
|
||||
function step() {
|
||||
if (idx >= tabs.length) return;
|
||||
const tab = tabs[idx++];
|
||||
if (tab === current || tabPanes.has(tab)) {
|
||||
step();
|
||||
return;
|
||||
}
|
||||
fetchTabHtml(tab)
|
||||
.then((html) => {
|
||||
if (!tabPanes.has(tab)) mountPane(tab, html);
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
setTimeout(step, 120);
|
||||
});
|
||||
}
|
||||
const ric = global.requestIdleCallback || function (fn) {
|
||||
setTimeout(fn, 1200);
|
||||
};
|
||||
ric(step);
|
||||
}
|
||||
|
||||
function clearTabCache() {
|
||||
tabPanes.forEach((pane) => pane.remove());
|
||||
tabPanes.clear();
|
||||
tabBooted.clear();
|
||||
}
|
||||
|
||||
function activateTab(tab, opts) {
|
||||
const options = opts || {};
|
||||
const revisit = !!options.revisit;
|
||||
const firstBoot = !tabBooted.has(tab);
|
||||
showPane(tab);
|
||||
setNavActive(tab);
|
||||
if (!options.skipUrl) syncUrl(tab, !!options.replace);
|
||||
runPageInit(tab, { revisit: !!options.revisit });
|
||||
notifyParentTabSwitch(tab);
|
||||
if (firstBoot) {
|
||||
bootPaneScripts(tab);
|
||||
runPageInit(tab, { revisit: false });
|
||||
return;
|
||||
}
|
||||
if (revisit) {
|
||||
document.body.setAttribute("data-page", tab);
|
||||
return;
|
||||
}
|
||||
runPageInit(tab, { revisit: false });
|
||||
}
|
||||
|
||||
async function loadTab(tab, opts) {
|
||||
@@ -237,6 +290,7 @@
|
||||
const pane = tabPanes.get(tab);
|
||||
if (pane) pane.remove();
|
||||
tabPanes.delete(tab);
|
||||
tabBooted.delete(tab);
|
||||
return loadTab(tab, { replace: true, skipUrl: true, force: true });
|
||||
}
|
||||
|
||||
@@ -354,6 +408,7 @@
|
||||
initBootPane();
|
||||
bindNav();
|
||||
runPageInit(getTab());
|
||||
preloadAllTabs();
|
||||
try {
|
||||
window.parent.postMessage({ type: "instance-frame-ready" }, "*");
|
||||
} catch (_) {}
|
||||
|
||||
@@ -32,8 +32,14 @@
|
||||
}
|
||||
|
||||
function onLiveEvent(data) {
|
||||
const reason = data && data.reason;
|
||||
const ver = Number(data && data.live_version) || 0;
|
||||
if (!ver || ver === localLiveVersion) return;
|
||||
if (!ver) return;
|
||||
if (reason === "connect") {
|
||||
localLiveVersion = ver;
|
||||
return;
|
||||
}
|
||||
if (ver === localLiveVersion) return;
|
||||
localLiveVersion = ver;
|
||||
refreshTabData(currentTab(), { silent: true });
|
||||
}
|
||||
@@ -67,14 +73,12 @@
|
||||
closeLiveStream();
|
||||
liveReconnectTimer = setTimeout(function () {
|
||||
connectLiveStream();
|
||||
refreshTabData(currentTab(), { silent: true });
|
||||
}, 8000);
|
||||
};
|
||||
}
|
||||
|
||||
function startLive() {
|
||||
if (!isEmbedShell()) return;
|
||||
refreshTabData(currentTab());
|
||||
connectLiveStream();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
const root = document.getElementById("options-root");
|
||||
if (!root) return;
|
||||
if (root.getAttribute("data-options-booted") === "1") return;
|
||||
root.setAttribute("data-options-booted", "1");
|
||||
|
||||
const panelCache = (window.__optionsPanelCache = window.__optionsPanelCache || {});
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ const ORDER_ENTRY_MODEL_CODE_TO_CATEGORY = {{ entry_model_code_to_category | toj
|
||||
<script src="/static/strategy_roll.js?v=6"></script>
|
||||
<script src="/static/key_monitor_form.js?v=2"></script>
|
||||
{% include 'embed_boot_scripts.html' %}
|
||||
<script src="/static/instance_live.js?v=1"></script>
|
||||
<script src="/static/instance_embed.js?v=10"></script>
|
||||
<script src="/static/instance_live.js?v=2"></script>
|
||||
<script src="/static/instance_embed.js?v=11"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user