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