feat(hub): mobile AI one-screen and dashboard monitor counts
Fix mobile AI to scroll only in the chat area. Dashboard cards show monitor item counts with expand-to-fullscreen and color-coded position floating P&L.
This commit is contained in:
@@ -652,41 +652,32 @@ def format_account_remark(ac: dict) -> str:
|
|||||||
return ";".join(parts)
|
return ";".join(parts)
|
||||||
|
|
||||||
|
|
||||||
def format_dashboard_account_lines(ac: dict) -> list[dict[str, Any]]:
|
def format_dashboard_account_detail(ac: dict) -> dict[str, Any]:
|
||||||
"""数据看板分户卡片:监控与持仓逐行展示(含浮盈亏数值供前端着色)。"""
|
"""数据看板分户卡片:监控仅数量,持仓逐行(含浮盈亏)。"""
|
||||||
lines: list[dict[str, Any]] = []
|
|
||||||
mon = ac.get("monitor_lines") or {}
|
mon = ac.get("monitor_lines") or {}
|
||||||
for row in (mon.get("keys") or [])[:3]:
|
position_lines: list[dict[str, Any]] = []
|
||||||
if row:
|
|
||||||
lines.append({"kind": "monitor", "text": str(row)})
|
|
||||||
for row in (mon.get("orders") or [])[:3]:
|
|
||||||
if row:
|
|
||||||
lines.append({"kind": "monitor", "text": str(row)})
|
|
||||||
for row in (mon.get("trends") or [])[:2]:
|
|
||||||
if row:
|
|
||||||
lines.append({"kind": "monitor", "text": str(row)})
|
|
||||||
for row in (mon.get("rolls") or [])[:2]:
|
|
||||||
if row:
|
|
||||||
lines.append({"kind": "monitor", "text": str(row)})
|
|
||||||
for p in _filter_open_positions(ac.get("positions") or []):
|
for p in _filter_open_positions(ac.get("positions") or []):
|
||||||
sym = p.get("symbol") or "?"
|
sym = p.get("symbol") or "?"
|
||||||
side = p.get("side") or "?"
|
side = p.get("side") or "?"
|
||||||
upnl = _position_float_pnl(p)
|
upnl = _position_float_pnl(p)
|
||||||
lines.append(
|
position_lines.append(
|
||||||
{
|
{
|
||||||
"kind": "position",
|
"kind": "position",
|
||||||
"text": f"{sym} {side}",
|
"text": f"{sym} {side}",
|
||||||
"pnl": round(upnl, 4),
|
"pnl": round(upnl, 4),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if not lines:
|
issues = [str(x) for x in (ac.get("issues") or [])[:3]]
|
||||||
issues = ac.get("issues") or []
|
return {
|
||||||
if issues:
|
"monitor_counts": {
|
||||||
for iss in issues[:3]:
|
"keys": len(mon.get("keys") or []),
|
||||||
lines.append({"kind": "issue", "text": str(iss)})
|
"orders": len(mon.get("orders") or []),
|
||||||
else:
|
"trends": len(mon.get("trends") or []),
|
||||||
lines.append({"kind": "empty", "text": "无"})
|
"rolls": len(mon.get("rolls") or []),
|
||||||
return lines
|
},
|
||||||
|
"position_lines": position_lines,
|
||||||
|
"issues": issues,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def collect_closed_trades_snapshot(
|
def collect_closed_trades_snapshot(
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from hub_ai.context import (
|
|||||||
build_daily_context,
|
build_daily_context,
|
||||||
collect_closed_trades_snapshot,
|
collect_closed_trades_snapshot,
|
||||||
format_account_remark,
|
format_account_remark,
|
||||||
format_dashboard_account_lines,
|
format_dashboard_account_detail,
|
||||||
)
|
)
|
||||||
from hub_ai.config import trading_day_reset_hour
|
from hub_ai.config import trading_day_reset_hour
|
||||||
from hub_trades_lib import current_trading_day
|
from hub_trades_lib import current_trading_day
|
||||||
@@ -64,7 +64,7 @@ def _enrich_account_row(ac: dict) -> dict:
|
|||||||
"float_pnl_u": ac.get("float_pnl_u"),
|
"float_pnl_u": ac.get("float_pnl_u"),
|
||||||
"open_position_count": ac.get("open_position_count"),
|
"open_position_count": ac.get("open_position_count"),
|
||||||
"remark": format_account_remark(ac),
|
"remark": format_account_remark(ac),
|
||||||
"remark_lines": format_dashboard_account_lines(ac),
|
**format_dashboard_account_detail(ac),
|
||||||
"issues": ac.get("issues") or [],
|
"issues": ac.get("issues") or [],
|
||||||
"daily_loss_pct": loss_pct,
|
"daily_loss_pct": loss_pct,
|
||||||
"loss_alert": loss_alert,
|
"loss_alert": loss_alert,
|
||||||
|
|||||||
@@ -3821,6 +3821,8 @@ html[data-theme="light"] button.danger {
|
|||||||
/* --- Hub AI 教练(整页一屏,内容区内滚动)--- */
|
/* --- Hub AI 教练(整页一屏,内容区内滚动)--- */
|
||||||
body.hub-page-ai {
|
body.hub-page-ai {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
height: 100dvh;
|
||||||
|
max-height: 100dvh;
|
||||||
}
|
}
|
||||||
body.hub-page-ai .app-shell {
|
body.hub-page-ai .app-shell {
|
||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
@@ -3831,6 +3833,13 @@ body.hub-page-ai .app-shell {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
body.hub-page-ai .app-shell > #page-ai {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
body.hub-page-ai .app-header {
|
body.hub-page-ai .app-header {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
@@ -3876,6 +3885,11 @@ body.hub-page-ai #page-ai {
|
|||||||
|
|
||||||
/* 手机 AI:须在 .ai-layout 双列定义之后,避免被覆盖成半屏 */
|
/* 手机 AI:须在 .ai-layout 双列定义之后,避免被覆盖成半屏 */
|
||||||
@media (max-width: 720px) {
|
@media (max-width: 720px) {
|
||||||
|
html:has(body.hub-page-ai) {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
body.hub-page-ai .app-shell {
|
body.hub-page-ai .app-shell {
|
||||||
padding-bottom: max(8px, env(safe-area-inset-bottom));
|
padding-bottom: max(8px, env(safe-area-inset-bottom));
|
||||||
height: var(--hub-vvh, 100dvh);
|
height: var(--hub-vvh, 100dvh);
|
||||||
@@ -3888,8 +3902,24 @@ body.hub-page-ai #page-ai {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body.hub-page-ai {
|
body.hub-page-ai {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
|
overscroll-behavior: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.hub-page-ai .app-header {
|
||||||
|
padding: 6px 0;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.hub-page-ai .top-nav a {
|
||||||
|
min-height: 34px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.hub-page-ai.hub-ai-keyboard-open .app-header,
|
body.hub-page-ai.hub-ai-keyboard-open .app-header,
|
||||||
@@ -4062,8 +4092,19 @@ body.hub-page-ai #page-ai {
|
|||||||
body.hub-page-ai .ai-chat-messages {
|
body.hub-page-ai .ai-chat-messages {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
max-height: none;
|
||||||
padding: 4px 2px 8px;
|
padding: 4px 2px 8px;
|
||||||
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.hub-page-ai .ai-layout[data-ai-mobile-tab="history"] .ai-chat-history-list {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
overscroll-behavior: contain;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3618,6 +3618,22 @@
|
|||||||
window.addEventListener("popstate", setActiveNav);
|
window.addEventListener("popstate", setActiveNav);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.hubOpenMonitorExpand = function hubOpenMonitorExpand(exId) {
|
||||||
|
const id = String(exId || "").trim();
|
||||||
|
if (!id) return;
|
||||||
|
expandedExchangeId = id;
|
||||||
|
sessionStorage.setItem("hub_expanded_ex", id);
|
||||||
|
if (currentPage() !== "monitor") {
|
||||||
|
history.pushState({}, "", "/monitor");
|
||||||
|
setActiveNav();
|
||||||
|
}
|
||||||
|
if (lastMonitorRows.length) {
|
||||||
|
openExchangeFullscreen(id);
|
||||||
|
} else {
|
||||||
|
void fetchMonitorBoardSnapshot({ showLoading: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
initAuth().then((ok) => {
|
initAuth().then((ok) => {
|
||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
initShellNav();
|
initShellNav();
|
||||||
|
|||||||
@@ -317,6 +317,75 @@ body.hub-page-dashboard .page#page-dashboard {
|
|||||||
color: var(--dash-muted);
|
color: var(--dash-muted);
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-ac-monitor-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-monitor-chip {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 3px 8px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.3;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-monitor-chip.dash-monitor-key {
|
||||||
|
color: #b8a0ff;
|
||||||
|
background: rgba(123, 97, 255, 0.18);
|
||||||
|
border-color: rgba(123, 97, 255, 0.42);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-monitor-chip.dash-monitor-order {
|
||||||
|
color: var(--dash-accent);
|
||||||
|
background: rgba(0, 212, 255, 0.14);
|
||||||
|
border-color: rgba(0, 212, 255, 0.38);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-monitor-chip.dash-monitor-trend {
|
||||||
|
color: var(--dash-ok);
|
||||||
|
background: rgba(0, 255, 157, 0.1);
|
||||||
|
border-color: rgba(0, 255, 157, 0.38);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-monitor-chip.dash-monitor-roll {
|
||||||
|
color: #ffb020;
|
||||||
|
background: rgba(255, 176, 32, 0.14);
|
||||||
|
border-color: rgba(255, 176, 32, 0.42);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-ac-expand-btn {
|
||||||
|
margin-left: auto;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid var(--dash-card-border);
|
||||||
|
background: color-mix(in srgb, var(--dash-accent) 8%, var(--dash-card-bg));
|
||||||
|
color: var(--dash-accent);
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-ac-expand-btn:hover {
|
||||||
|
border-color: var(--dash-accent);
|
||||||
|
background: color-mix(in srgb, var(--dash-accent) 14%, var(--dash-card-bg));
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-ac-positions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 3px;
|
gap: 3px;
|
||||||
@@ -346,10 +415,33 @@ body.hub-page-dashboard .page#page-dashboard {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dash-ac-remark-pos .pos {
|
||||||
|
color: var(--dash-ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-ac-remark-pos .neg {
|
||||||
|
color: var(--dash-warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-ac-remark-empty {
|
||||||
|
color: var(--dash-muted);
|
||||||
|
}
|
||||||
|
|
||||||
.dash-ac-remark-issue {
|
.dash-ac-remark-issue {
|
||||||
color: var(--dash-warn);
|
color: var(--dash-warn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] .dash-monitor-chip.dash-monitor-key {
|
||||||
|
color: #5b4fc7;
|
||||||
|
background: rgba(91, 79, 199, 0.1);
|
||||||
|
border-color: rgba(91, 79, 199, 0.28);
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] .dash-monitor-chip.dash-monitor-trend {
|
||||||
|
background: rgba(10, 143, 92, 0.1);
|
||||||
|
border-color: rgba(10, 143, 92, 0.28);
|
||||||
|
}
|
||||||
|
|
||||||
.dash-table-wrap {
|
.dash-table-wrap {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
max-height: min(52vh, 480px);
|
max-height: min(52vh, 480px);
|
||||||
|
|||||||
@@ -90,17 +90,43 @@
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderRemarkLines(ac) {
|
function renderMonitorCountChips(counts) {
|
||||||
const lines = Array.isArray(ac && ac.remark_lines) ? ac.remark_lines : [];
|
const mc = counts || {};
|
||||||
if (!lines.length) {
|
const chips = [];
|
||||||
const fallback = esc((ac && ac.remark) || "—");
|
const keys = Number(mc.keys) || 0;
|
||||||
return `<div class="dash-ac-remark"><div class="dash-ac-remark-line">${fallback}</div></div>`;
|
const orders = Number(mc.orders) || 0;
|
||||||
|
const trends = Number(mc.trends) || 0;
|
||||||
|
const rolls = Number(mc.rolls) || 0;
|
||||||
|
if (keys > 0) chips.push(`<span class="dash-monitor-chip dash-monitor-key">关键位 ${keys}</span>`);
|
||||||
|
if (orders > 0) {
|
||||||
|
chips.push(`<span class="dash-monitor-chip dash-monitor-order">下单监控 ${orders}</span>`);
|
||||||
}
|
}
|
||||||
return `<div class="dash-ac-remark">${lines
|
if (trends > 0) chips.push(`<span class="dash-monitor-chip dash-monitor-trend">趋势回调 ${trends}</span>`);
|
||||||
|
if (rolls > 0) chips.push(`<span class="dash-monitor-chip dash-monitor-roll">顺势加仓 ${rolls}</span>`);
|
||||||
|
return chips;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAccountDetail(ac) {
|
||||||
|
const counts = (ac && ac.monitor_counts) || {};
|
||||||
|
const positions = Array.isArray(ac && ac.position_lines) ? ac.position_lines : [];
|
||||||
|
const issues = Array.isArray(ac && ac.issues) ? ac.issues : [];
|
||||||
|
const exId = ac && ac.id != null ? String(ac.id) : "";
|
||||||
|
const chips = renderMonitorCountChips(counts);
|
||||||
|
const expandBtn = exId
|
||||||
|
? `<button type="button" class="dash-ac-expand-btn" data-dash-ex-id="${esc(exId)}" title="放大查看监控详情" aria-label="放大查看监控详情">` +
|
||||||
|
`<svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true"><path fill="currentColor" d="M15 3h6v6h-2V6.41l-7.29 7.3-1.42-1.42 7.3-7.29H15V3zM3 9h2v10h10v2H3V9z"/></svg>` +
|
||||||
|
`</button>`
|
||||||
|
: "";
|
||||||
|
const monitorRow =
|
||||||
|
chips.length || expandBtn
|
||||||
|
? `<div class="dash-ac-monitor-row">${chips.join("")}${expandBtn}</div>`
|
||||||
|
: "";
|
||||||
|
let posHtml = "";
|
||||||
|
if (positions.length) {
|
||||||
|
posHtml = positions
|
||||||
.map((ln) => {
|
.map((ln) => {
|
||||||
const kind = ln && ln.kind;
|
|
||||||
const text = esc((ln && ln.text) || "");
|
const text = esc((ln && ln.text) || "");
|
||||||
if (kind === "position" && ln.pnl != null && Number.isFinite(Number(ln.pnl))) {
|
if (ln.pnl != null && Number.isFinite(Number(ln.pnl))) {
|
||||||
const pnl = Number(ln.pnl);
|
const pnl = Number(ln.pnl);
|
||||||
return (
|
return (
|
||||||
`<div class="dash-ac-remark-line dash-ac-remark-pos">` +
|
`<div class="dash-ac-remark-line dash-ac-remark-pos">` +
|
||||||
@@ -108,15 +134,28 @@
|
|||||||
`</div>`
|
`</div>`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const cls =
|
return `<div class="dash-ac-remark-line dash-ac-remark-pos">${text}</div>`;
|
||||||
kind === "monitor"
|
|
||||||
? "dash-ac-remark-line dash-ac-remark-mon"
|
|
||||||
: kind === "issue"
|
|
||||||
? "dash-ac-remark-line dash-ac-remark-issue"
|
|
||||||
: "dash-ac-remark-line";
|
|
||||||
return `<div class="${cls}">${text}</div>`;
|
|
||||||
})
|
})
|
||||||
.join("")}</div>`;
|
.join("");
|
||||||
|
} else if (!chips.length && !issues.length) {
|
||||||
|
posHtml = `<div class="dash-ac-remark-line dash-ac-remark-empty">无持仓</div>`;
|
||||||
|
}
|
||||||
|
const issueHtml = issues
|
||||||
|
.map((text) => `<div class="dash-ac-remark-line dash-ac-remark-issue">${esc(text)}</div>`)
|
||||||
|
.join("");
|
||||||
|
return `<div class="dash-ac-remark">${monitorRow}<div class="dash-ac-positions">${posHtml}</div>${issueHtml}</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindDashboardExpand() {
|
||||||
|
if (!elAccounts) return;
|
||||||
|
elAccounts.querySelectorAll(".dash-ac-expand-btn").forEach((btn) => {
|
||||||
|
btn.addEventListener("click", (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
const id = btn.getAttribute("data-dash-ex-id");
|
||||||
|
if (id && window.hubOpenMonitorExpand) window.hubOpenMonitorExpand(id);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderAccounts(accounts, threshold) {
|
function renderAccounts(accounts, threshold) {
|
||||||
@@ -158,10 +197,11 @@
|
|||||||
<div class="dash-ac-metric"><span>浮盈亏</span><strong class="${pnlClass(floatPnl)}">${pnlSigned(floatPnl, 2)}</strong></div>
|
<div class="dash-ac-metric"><span>浮盈亏</span><strong class="${pnlClass(floatPnl)}">${pnlSigned(floatPnl, 2)}</strong></div>
|
||||||
</div>
|
</div>
|
||||||
${lossBar}
|
${lossBar}
|
||||||
${renderRemarkLines(ac)}
|
${renderAccountDetail(ac)}
|
||||||
</article>`;
|
</article>`;
|
||||||
})
|
})
|
||||||
.join("");
|
.join("");
|
||||||
|
bindDashboardExpand();
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderTrades(trades, accounts) {
|
function renderTrades(trades, accounts) {
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" media="print" onload="this.media='all'" />
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" media="print" onload="this.media='all'" />
|
||||||
<noscript><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" /></noscript>
|
<noscript><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" /></noscript>
|
||||||
<link rel="stylesheet" href="/assets/app.css?v=20260611-hub-ai-tabs" />
|
<link rel="stylesheet" href="/assets/app.css?v=20260612-hub-ai-one-screen" />
|
||||||
<link rel="stylesheet" href="/assets/dashboard.css?v=20260611-dash-remark-lines" />
|
<link rel="stylesheet" href="/assets/dashboard.css?v=20260612-dash-monitor-count" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="app-bg" aria-hidden="true"></div>
|
<div class="app-bg" aria-hidden="true"></div>
|
||||||
@@ -555,8 +555,8 @@
|
|||||||
<script src="/assets/chart.js?v=20260609-market-day-split"></script>
|
<script src="/assets/chart.js?v=20260609-market-day-split"></script>
|
||||||
<script src="/assets/archive.js?v=20260608-hub-archive-history"></script>
|
<script src="/assets/archive.js?v=20260608-hub-archive-history"></script>
|
||||||
<script src="/assets/funds.js?v=20260609-hub-funds-fold"></script>
|
<script src="/assets/funds.js?v=20260609-hub-funds-fold"></script>
|
||||||
<script src="/assets/dashboard.js?v=20260611-dash-remark-lines"></script>
|
<script src="/assets/dashboard.js?v=20260612-dash-monitor-count"></script>
|
||||||
<script src="/assets/ai_review_render.js?v=2"></script>
|
<script src="/assets/ai_review_render.js?v=2"></script>
|
||||||
<script src="/assets/app.js?v=20260611-hub-ai-tabs"></script>
|
<script src="/assets/app.js?v=20260612-hub-monitor-expand"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user