feat(hub): mobile AI tabs and dashboard position lines
Mobile AI coach uses four top tabs (trading, general, history, new) with single-panel view and wider desktop history. Dashboard account cards show key levels and positions one per line with colored float PnL. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1038,22 +1038,43 @@
|
||||
}
|
||||
|
||||
const AI_MOBILE_TAB_KEY = "hub_ai_mobile_tab";
|
||||
const AI_MOBILE_CHAT_TABS = new Set(["trading", "general"]);
|
||||
|
||||
function normalizeAiMobileTab(tab) {
|
||||
const raw = (tab || "").trim().toLowerCase();
|
||||
if (raw === "chat") return "trading";
|
||||
if (AI_MOBILE_CHAT_TABS.has(raw) || raw === "history") return raw;
|
||||
return "trading";
|
||||
}
|
||||
|
||||
function applyAiMobileTab(tab) {
|
||||
const layout = document.querySelector(".ai-layout");
|
||||
const tabs = document.querySelectorAll(".ai-mobile-tab");
|
||||
if (!layout) return;
|
||||
const mobile = isMobileLayout();
|
||||
const active = mobile ? tab || localStorage.getItem(AI_MOBILE_TAB_KEY) || "chat" : "both";
|
||||
if (mobile) layout.dataset.aiMobileTab = active;
|
||||
else delete layout.dataset.aiMobileTab;
|
||||
if (!mobile) {
|
||||
delete layout.dataset.aiMobileTab;
|
||||
tabs.forEach((btn) => {
|
||||
btn.classList.remove("is-active");
|
||||
btn.setAttribute("aria-selected", "false");
|
||||
});
|
||||
return;
|
||||
}
|
||||
const active = normalizeAiMobileTab(
|
||||
tab || localStorage.getItem(AI_MOBILE_TAB_KEY) || "trading"
|
||||
);
|
||||
layout.dataset.aiMobileTab = active;
|
||||
tabs.forEach((btn) => {
|
||||
const on = mobile && btn.dataset.aiTab === active;
|
||||
const t = btn.dataset.aiTab || "";
|
||||
const on = t === active;
|
||||
btn.classList.toggle("is-active", on);
|
||||
btn.setAttribute("aria-selected", on ? "true" : "false");
|
||||
});
|
||||
if (mobile && active === "chat") scrollAiChatToEnd();
|
||||
if (mobile && active === "history") {
|
||||
if (AI_MOBILE_CHAT_TABS.has(active)) {
|
||||
updateAiBotTabs(active);
|
||||
scrollAiChatToEnd();
|
||||
}
|
||||
if (active === "history") {
|
||||
const hist = document.getElementById("ai-chat-history-list");
|
||||
if (hist) hist.scrollTop = 0;
|
||||
}
|
||||
@@ -1064,10 +1085,16 @@
|
||||
if (!tabs.length) return;
|
||||
tabs.forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
const tab = btn.dataset.aiTab || "chat";
|
||||
const tab = btn.dataset.aiTab || "trading";
|
||||
if (tab === "new") {
|
||||
const prev = normalizeAiMobileTab(localStorage.getItem(AI_MOBILE_TAB_KEY) || "trading");
|
||||
const botMode = prev === "general" ? "general" : "trading";
|
||||
void newAiChat(botMode);
|
||||
return;
|
||||
}
|
||||
localStorage.setItem(AI_MOBILE_TAB_KEY, tab);
|
||||
applyAiMobileTab(tab);
|
||||
if (tab === "chat") {
|
||||
if (AI_MOBILE_CHAT_TABS.has(tab)) {
|
||||
const input = document.getElementById("ai-chat-input");
|
||||
if (input && isMobileLayout()) input.focus();
|
||||
}
|
||||
@@ -3388,8 +3415,13 @@
|
||||
aiChatSessionsCache = j.sessions || [];
|
||||
renderAiChatMessages(aiChatSessionCache);
|
||||
renderAiChatHistory(aiChatSessionsCache);
|
||||
updateAiBotTabs((aiChatSessionCache && aiChatSessionCache.bot_mode) || "trading");
|
||||
applyAiMobileTab("chat");
|
||||
const mode =
|
||||
(aiChatSessionCache && aiChatSessionCache.bot_mode) === "general" ? "general" : "trading";
|
||||
updateAiBotTabs(mode);
|
||||
if (isMobileLayout()) {
|
||||
localStorage.setItem(AI_MOBILE_TAB_KEY, mode);
|
||||
applyAiMobileTab(mode);
|
||||
}
|
||||
scrollAiChatToEnd();
|
||||
} catch (e) {
|
||||
showToast(String(e), true);
|
||||
@@ -3421,7 +3453,8 @@
|
||||
async function loadAiPage() {
|
||||
applyAiMobileTab();
|
||||
await loadAiChatSession();
|
||||
if (isMobileLayout() && (localStorage.getItem(AI_MOBILE_TAB_KEY) || "chat") === "chat") {
|
||||
const mobTab = normalizeAiMobileTab(localStorage.getItem(AI_MOBILE_TAB_KEY) || "trading");
|
||||
if (isMobileLayout() && AI_MOBILE_CHAT_TABS.has(mobTab)) {
|
||||
const input = document.getElementById("ai-chat-input");
|
||||
if (input && !aiChatLoading) {
|
||||
setTimeout(() => input.focus(), 80);
|
||||
@@ -3443,7 +3476,10 @@
|
||||
renderAiChatMessages(aiChatSessionCache);
|
||||
renderAiChatHistory(aiChatSessionsCache);
|
||||
updateAiBotTabs(mode);
|
||||
applyAiMobileTab("chat");
|
||||
if (isMobileLayout()) {
|
||||
localStorage.setItem(AI_MOBILE_TAB_KEY, mode);
|
||||
applyAiMobileTab(mode);
|
||||
}
|
||||
showToast(mode === "general" ? "已开始普通聊天" : "已开始交易教练对话");
|
||||
} catch (e) {
|
||||
showToast(String(e), true);
|
||||
|
||||
Reference in New Issue
Block a user