feat: add light/dark theme to exchange instances with hub SSO sync

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-04 12:52:27 +08:00
parent 6f8f0968c8
commit d14c629778
24 changed files with 3134 additions and 2369 deletions
+8 -1
View File
@@ -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,
+13
View File
@@ -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() {
+3 -3
View File
@@ -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>
+10
View File
@@ -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;
}