feat: add light/dark theme to exchange instances with hub SSO sync
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1057,7 +1057,11 @@ def _require_hub_logged_in(request: Request) -> None:
|
||||
|
||||
@app.get("/api/instance/open-url")
|
||||
def api_instance_open_url(
|
||||
request: Request, exchange_id: str, next: str = "/", embed: str = ""
|
||||
request: Request,
|
||||
exchange_id: str,
|
||||
next: str = "/",
|
||||
embed: str = "",
|
||||
hub_theme: str = "",
|
||||
):
|
||||
"""已登录中控时生成实例 SSO 打开链接(2h 有效、单次使用,复用 HUB_BRIDGE_TOKEN)。"""
|
||||
_require_hub_logged_in(request)
|
||||
@@ -1079,6 +1083,9 @@ def api_instance_open_url(
|
||||
params = {"token": token, "next": nxt}
|
||||
if (embed or "").strip().lower() in ("1", "true", "yes", "on"):
|
||||
params["embed"] = "1"
|
||||
ht = (hub_theme or "").strip().lower()
|
||||
if ht in ("light", "dark"):
|
||||
params["hub_theme"] = ht
|
||||
q = urlencode(params)
|
||||
return {
|
||||
"ok": True,
|
||||
|
||||
@@ -56,6 +56,9 @@
|
||||
const next = nextPath || "/";
|
||||
const q = new URLSearchParams({ exchange_id: String(exchangeId), next });
|
||||
if (options.embed) q.set("embed", "1");
|
||||
if (globalThis.HubTheme && typeof HubTheme.get === "function") {
|
||||
q.set("hub_theme", HubTheme.get());
|
||||
}
|
||||
const r = await apiFetch("/api/instance/open-url?" + q.toString());
|
||||
const j = await r.json();
|
||||
if (!j.ok || !j.url) {
|
||||
@@ -135,6 +138,16 @@
|
||||
shell.classList.remove("hidden");
|
||||
shell.setAttribute("aria-hidden", "false");
|
||||
document.body.classList.add("hub-instance-frame-open");
|
||||
frame.addEventListener("load", function syncInstanceFrameTheme() {
|
||||
try {
|
||||
if (globalThis.HubTheme && typeof HubTheme.get === "function" && frame.contentWindow) {
|
||||
frame.contentWindow.postMessage(
|
||||
{ type: "hub-theme-sync", theme: HubTheme.get() },
|
||||
"*"
|
||||
);
|
||||
}
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
|
||||
function closeInstanceFrame() {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html lang="zh-CN" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script src="/assets/theme.js?v=20260604-hub-theme4"></script>
|
||||
<script src="/assets/theme.js?v=20260604-hub-inst-theme"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<meta name="theme-color" content="#0b0e18" />
|
||||
<meta name="apple-mobile-web-app-title" content="中控" />
|
||||
@@ -248,7 +248,7 @@
|
||||
|
||||
<div id="toast"></div>
|
||||
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
||||
<script src="/assets/chart.js?v=20260604-hub-chart-sse"></script>
|
||||
<script src="/assets/app.js?v=20260604-hub-chart-sse"></script>
|
||||
<script src="/assets/chart.js?v=20260604-hub-inst-theme"></script>
|
||||
<script src="/assets/app.js?v=20260604-hub-inst-theme"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -15,6 +15,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
function broadcastThemeToInstances() {
|
||||
const msg = { type: "hub-theme-sync", theme: get() };
|
||||
document.querySelectorAll("iframe#instance-frame, iframe.instance-frame").forEach((frame) => {
|
||||
try {
|
||||
if (frame.contentWindow) frame.contentWindow.postMessage(msg, "*");
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
|
||||
function apply(theme) {
|
||||
const t = normalize(theme);
|
||||
const root = document.documentElement;
|
||||
@@ -26,6 +35,7 @@
|
||||
if (meta) meta.setAttribute("content", META[t]);
|
||||
root.style.colorScheme = t;
|
||||
document.dispatchEvent(new CustomEvent("hub-theme-change", { detail: { theme: t } }));
|
||||
broadcastThemeToInstances();
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user