fix: reconnect mentor page into the project shell
Remove the mentor immersive rules in shared/shell.css that hid the module nav, header actions, market tape, overview strip and status bar while mentorView was active, which made the page look like an independent site. Mentor now stays inside the canonical 小白复盘 shell and owns its own "问师 + 数据日期" title header inside #mentorView (B-92 content intact). Also fix the shell's latent 1024px overflow: .main min-width 1080 now only applies at >=1280, and the overview strip scrolls internally instead of clipping its right side when it is wider than the content column. Update the contract and e2e baselines to assert the shell stays visible and usable on mentor, page switching leaves no residue, and add a dedicated shell-integration e2e test. Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
multica-agent
parent
8fa52b43a8
commit
9f8fbc1937
@@ -372,7 +372,7 @@
|
||||
"css_layers": [
|
||||
"/shared/tokens.css?v=20260818-2",
|
||||
"/shared/base.css?v=20260806-1",
|
||||
"/shared/shell.css?v=20260818-2",
|
||||
"/shared/shell.css?v=20260818-3",
|
||||
"/shared/auth.css?v=20260806-1",
|
||||
"/shared/components/controls.css?v=20260806-1",
|
||||
"/shared/components/navigation.css?v=20260806-1",
|
||||
@@ -390,7 +390,7 @@
|
||||
"/pages/popularity/foundation.css?v=20260806-1",
|
||||
"/pages/dragon-tiger/foundation.css?v=20260806-1",
|
||||
"/pages/screener/foundation.css?v=20260806-2",
|
||||
"/pages/mentor/foundation.css?v=20260818-2",
|
||||
"/pages/mentor/foundation.css?v=20260818-3",
|
||||
"/pages/heaven/foundation.css?v=20260806-2",
|
||||
"/pages/review/foundation.css?v=20260806-1"
|
||||
],
|
||||
@@ -451,8 +451,8 @@
|
||||
},
|
||||
{
|
||||
"path": "frontend/shared/shell.css",
|
||||
"bytes": 54781,
|
||||
"lines": 3348
|
||||
"bytes": 52879,
|
||||
"lines": 3274
|
||||
},
|
||||
{
|
||||
"path": "backend/features/heaven/engine.py",
|
||||
@@ -606,8 +606,8 @@
|
||||
},
|
||||
{
|
||||
"path": "frontend/pages/mentor/page.html",
|
||||
"bytes": 8117,
|
||||
"lines": 110
|
||||
"bytes": 8357,
|
||||
"lines": 116
|
||||
},
|
||||
{
|
||||
"path": "backend/data/providers/tushare_daily.py",
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@
|
||||
</script>
|
||||
<link rel="stylesheet" href="/shared/tokens.css?v=20260818-2">
|
||||
<link rel="stylesheet" href="/shared/base.css?v=20260806-1">
|
||||
<link rel="stylesheet" href="/shared/shell.css?v=20260818-2">
|
||||
<link rel="stylesheet" href="/shared/shell.css?v=20260818-3">
|
||||
<link rel="stylesheet" href="/shared/auth.css?v=20260806-1">
|
||||
<link rel="stylesheet" href="/shared/components/controls.css?v=20260806-1">
|
||||
<link rel="stylesheet" href="/shared/components/navigation.css?v=20260806-1">
|
||||
@@ -37,7 +37,7 @@
|
||||
<link rel="stylesheet" href="/pages/popularity/foundation.css?v=20260806-1">
|
||||
<link rel="stylesheet" href="/pages/dragon-tiger/foundation.css?v=20260806-1">
|
||||
<link rel="stylesheet" href="/pages/screener/foundation.css?v=20260806-2">
|
||||
<link rel="stylesheet" href="/pages/mentor/foundation.css?v=20260818-2">
|
||||
<link rel="stylesheet" href="/pages/mentor/foundation.css?v=20260818-3">
|
||||
<link rel="stylesheet" href="/pages/heaven/foundation.css?v=20260806-2">
|
||||
<link rel="stylesheet" href="/pages/review/foundation.css?v=20260806-1">
|
||||
</head>
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
["heaven", "/pages/heaven/page.html?v=20260803-1", ["heavenView"]],
|
||||
["ladder", "/pages/ladder/page.html?v=20260803-1", ["ladderView"]],
|
||||
["screener", "/pages/screener/page.html?v=20260804-1", ["screenerView", "screenerTrackingView"]],
|
||||
["mentor", "/pages/mentor/page.html?v=20260818-2", ["mentorView"]],
|
||||
["mentor", "/pages/mentor/page.html?v=20260818-3", ["mentorView"]],
|
||||
["rotation", "/pages/rotation/page.html?v=20260803-1", ["rotationView"]],
|
||||
["auction", "/pages/auction/page.html?v=20260803-1", ["auctionView"]],
|
||||
["themes", "/pages/themes/page.html?v=20260803-1", ["themeLibraryView"]],
|
||||
@@ -81,7 +81,7 @@
|
||||
"/pages/popularity/page.js?v=20260729-1",
|
||||
"/pages/dragon-tiger/page.js?v=20260806-1",
|
||||
"/pages/screener/page.js?v=20260806-1",
|
||||
"/pages/mentor/page.js?v=20260818-2",
|
||||
"/pages/mentor/page.js?v=20260818-3",
|
||||
"/pages/heaven/page.js?v=20260729-1",
|
||||
"/pages/review/page.js?v=20260729-1",
|
||||
"/shared/state.js?v=20260729-1",
|
||||
|
||||
Vendored
+43
@@ -87,6 +87,45 @@
|
||||
margin-bottom: var(--space-8);
|
||||
}
|
||||
|
||||
#mentorView .mentor-page-header {
|
||||
flex: 0 0 56px;
|
||||
height: 56px;
|
||||
min-height: 56px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
border-bottom: 1px solid var(--qp-border);
|
||||
background: var(--qp-bg-header);
|
||||
}
|
||||
|
||||
#mentorView .mentor-page-title {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
#mentorView .mentor-page-title h2 {
|
||||
flex: 0 0 auto;
|
||||
margin: 0;
|
||||
color: var(--qp-text-1);
|
||||
font-size: 18px;
|
||||
font-weight: var(--font-weight-semibold);
|
||||
line-height: 1.3;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
#mentorView .mentor-page-title p {
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
color: var(--qp-text-3);
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#mentorView .mentor-layout {
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
@@ -1322,6 +1361,10 @@
|
||||
border-bottom: 1px solid var(--qp-border);
|
||||
}
|
||||
|
||||
#mentorView .mentor-page-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mentorView .mentor-chat-panel {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
<section id="mentorView" class="workspace-view page member-feature-view redesigned-mentor-view mentor-page-header">
|
||||
<section id="mentorView" class="workspace-view page member-feature-view redesigned-mentor-view">
|
||||
<div class="member-gate" hidden><div class="member-gate-icon"><i data-lucide="lock-keyhole"></i></div><div><strong>问师仅对会员开放</strong><span>开通会员后可使用思维模型进行对话。会员状态可从顶部账号标识进入。</span></div></div>
|
||||
<div id="mentorNotice" class="inline-notice" hidden></div>
|
||||
<header class="mentor-page-header" aria-label="问师">
|
||||
<div class="mentor-page-title">
|
||||
<h2>问师</h2>
|
||||
<p id="mentorPageSubtitle">与不同交易思维模型持续对话 · 数据日期 --</p>
|
||||
</div>
|
||||
</header>
|
||||
<div class="mentor-layout">
|
||||
<aside class="mentor-sidebar" aria-label="思维模型目录">
|
||||
<div class="mentor-directory-tools">
|
||||
|
||||
@@ -66,7 +66,7 @@ function renderMentorWorkspace() {
|
||||
const setup = state.mentorSetup;
|
||||
if (!setup) return;
|
||||
const selected = setup.mentors.find((item) => item.id === state.selectedMentorId) || null;
|
||||
setText("currentPageSubtitle", `与不同交易思维模型持续对话 · 数据日期 ${displayCompactDate(setup.trade_date)}`);
|
||||
setText("mentorPageSubtitle", `与不同交易思维模型持续对话 · 数据日期 ${displayCompactDate(setup.trade_date)}`);
|
||||
setText("activeMentorName", selected?.name || "--");
|
||||
setText("activeMentorAvatar", mentorAvatarText(selected));
|
||||
applyMentorAvatarTone(document.querySelector("#activeMentorAvatar"), mentorAvatarToneClass(selected));
|
||||
|
||||
+20
-94
@@ -1756,7 +1756,11 @@ body.sidebar-collapsed .module-nav .module-tab {
|
||||
|
||||
padding: 0px 16px;
|
||||
|
||||
overflow: hidden;
|
||||
overflow-x: auto;
|
||||
|
||||
overflow-y: hidden;
|
||||
|
||||
scrollbar-width: none;
|
||||
|
||||
border-top: 0px;
|
||||
|
||||
@@ -1775,6 +1779,10 @@ body.sidebar-collapsed .module-nav .module-tab {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.overview-strip::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.overview-strip .row {
|
||||
display: flex;
|
||||
|
||||
@@ -1784,6 +1792,8 @@ body.sidebar-collapsed .module-nav .module-tab {
|
||||
|
||||
padding: 7px var(--page-pad-x);
|
||||
|
||||
flex-shrink: 0;
|
||||
|
||||
font-size: 12px;
|
||||
|
||||
color: var(--sub);
|
||||
@@ -2266,7 +2276,13 @@ body.sidebar-collapsed .status-bar {
|
||||
.main {
|
||||
margin-left: var(--sidebar-width);
|
||||
|
||||
min-width: 1080px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.main {
|
||||
min-width: 1080px;
|
||||
}
|
||||
}
|
||||
|
||||
.app-header {
|
||||
@@ -3223,8 +3239,8 @@ body.sidebar-collapsed .status-bar {
|
||||
}
|
||||
}
|
||||
|
||||
/* Dedicated mentor workspace (final day/night spec). Only the active mentor
|
||||
view enables this immersive state; other pages keep the canonical shell. */
|
||||
/* Generic page title slot in the shell header. Kept hidden so each page owns
|
||||
its own in-view title header (mentor renders one inside #mentorView). */
|
||||
.app-page-context {
|
||||
min-width: 0;
|
||||
flex: 1 1 auto;
|
||||
@@ -3256,93 +3272,3 @@ body.sidebar-collapsed .status-bar {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
body[data-active-view="mentorView"] .main {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
margin-left: 0;
|
||||
height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body[data-active-view="mentorView"] .module-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body[data-active-view="mentorView"] .status-bar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body[data-active-view="mentorView"] .market-tape,
|
||||
body[data-active-view="mentorView"] .mobile-page-context {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body[data-active-view="mentorView"] .overview-strip {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body[data-active-view="mentorView"] .app-header {
|
||||
flex: 0 0 56px;
|
||||
height: 56px;
|
||||
min-height: 56px;
|
||||
gap: 24px;
|
||||
padding: 0 20px;
|
||||
background: #F5F6F7;
|
||||
}
|
||||
|
||||
:root[data-theme="dark"] body[data-active-view="mentorView"] .app-header {
|
||||
background: #141519;
|
||||
}
|
||||
|
||||
body[data-active-view="mentorView"] .app-page-context {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
body[data-active-view="mentorView"] .header-actions > :is(.global-search-button, .alert-button, .assistant-button, .header-menu-button, .header-date-group, .header-command-group) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body[data-active-view="mentorView"] .theme-toggle {
|
||||
width: auto;
|
||||
height: 32px;
|
||||
min-height: 32px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 0 12px;
|
||||
border-radius: var(--radius-md);
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
font-size: var(--font-size-label);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
body[data-active-view="mentorView"] .theme-toggle .lucide {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
body[data-active-view="mentorView"] #themeModeText {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
body[data-active-view="mentorView"] .app-main {
|
||||
flex: 1 1 auto;
|
||||
height: calc(100dvh - 56px);
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body[data-active-view="mentorView"] .workspace-view.active-view {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2838,22 +2838,55 @@ test("mentor directory exposes evidence filters and private owner metadata", asy
|
||||
const searchField = await page.locator("#mentorView .mentor-search-field").boundingBox();
|
||||
expect(searchField.width).toBeGreaterThan(160);
|
||||
await expect(page.locator("#mentorSearchInput")).toHaveAttribute("placeholder", "搜索联系人或标签");
|
||||
await expect(page.locator("#tradeDate")).toBeHidden();
|
||||
await expect(page.locator("#syncButton")).toBeHidden();
|
||||
await expect(page.locator("#settingsButton")).toBeHidden();
|
||||
await expect(page.locator("#headerMenuButton")).toBeHidden();
|
||||
await expect(page.locator("#accountButton")).toBeHidden();
|
||||
await expect(page.locator(".module-nav")).toBeVisible();
|
||||
await expect(page.locator(".module-tab.active")).toHaveAttribute("data-view", "mentorView");
|
||||
await expect(page.locator(".market-tape")).toBeVisible();
|
||||
await expect(page.locator("#tradeDate")).toBeVisible();
|
||||
await expect(page.locator(".overview-strip")).toBeVisible();
|
||||
await expect(page.locator(".status-bar")).toBeVisible();
|
||||
await expect(page.locator("#themeToggle")).toBeVisible();
|
||||
await expect(page.locator("#mentorView .mentor-page-title h2")).toHaveText("问师");
|
||||
await expect(page.locator("#mentorPageSubtitle")).toContainText("数据日期");
|
||||
expect(await page.evaluate(() => document.documentElement.scrollHeight - window.innerHeight)).toBeLessThanOrEqual(1);
|
||||
await page.setViewportSize({ width: 1920, height: 947 });
|
||||
const expandedMentorLayout = await page.locator("#mentorView .mentor-layout").boundingBox();
|
||||
const footerBar = await page.locator("#mentorView .mentor-workspace-footer").boundingBox();
|
||||
expect(Math.abs(footerBar.y + footerBar.height - 947)).toBeLessThanOrEqual(1);
|
||||
const shellStatusBar = await page.locator(".status-bar").boundingBox();
|
||||
expect(Math.abs(shellStatusBar.y + shellStatusBar.height - 947)).toBeLessThanOrEqual(1);
|
||||
expect(footerBar.y + footerBar.height).toBeLessThanOrEqual(shellStatusBar.y);
|
||||
expect(Math.abs(expandedMentorLayout.y + expandedMentorLayout.height - footerBar.y)).toBeLessThanOrEqual(1);
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth - window.innerWidth)).toBeLessThanOrEqual(1);
|
||||
expect(await page.evaluate(() => document.documentElement.scrollHeight - window.innerHeight)).toBeLessThanOrEqual(1);
|
||||
});
|
||||
|
||||
test("mentor stays inside the project shell and switching pages leaves no residue", async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1440, height: 900 });
|
||||
await mockApplication(page, session("admin", true));
|
||||
await page.goto("/index.html");
|
||||
await page.locator('[data-view="mentorView"]').first().click();
|
||||
|
||||
await expect(page.locator("#mentorView")).toHaveClass(/active-view/);
|
||||
await expect(page.locator(".module-nav")).toBeVisible();
|
||||
await expect(page.locator(".module-tab.active")).toHaveAttribute("data-view", "mentorView");
|
||||
await expect(page.locator("#mentorList .mentor-option")).toHaveCount(22);
|
||||
await expect(page.locator("#mentorView .mentor-page-title h2")).toHaveText("问师");
|
||||
|
||||
await page.locator('[data-view="limitPool"]').first().click();
|
||||
await expect(page.locator("#limitPool")).toHaveClass(/active-view/);
|
||||
await expect(page.locator("#mentorView")).not.toHaveClass(/active-view/);
|
||||
await expect(page.locator("#mentorView")).not.toBeVisible();
|
||||
await expect(page.locator(".module-nav")).toBeVisible();
|
||||
await expect(page.locator(".module-tab.active")).toHaveAttribute("data-view", "limitPool");
|
||||
|
||||
await page.locator('[data-view="mentorView"]').first().click();
|
||||
await expect(page.locator("#mentorView")).toHaveClass(/active-view/);
|
||||
await expect(page.locator(".module-nav")).toBeVisible();
|
||||
await expect(page.locator(".module-tab.active")).toHaveAttribute("data-view", "mentorView");
|
||||
await expect(page.locator("#mentorList .mentor-option")).toHaveCount(22);
|
||||
await expect(page.locator("#mentorPageSubtitle")).toContainText("数据日期");
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth - window.innerWidth)).toBeLessThanOrEqual(1);
|
||||
});
|
||||
|
||||
test("mentor keeps two columns and opens the profile floating dialog on desktop", async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1440, height: 900 });
|
||||
await mockApplication(page, session("admin", true));
|
||||
|
||||
@@ -339,10 +339,11 @@ class FrontendContractTests(unittest.TestCase):
|
||||
shell_styles = (STATIC_DIR / "shared" / "shell.css").read_text(encoding="utf-8")
|
||||
mentor_html = (STATIC_DIR / "pages" / "mentor" / "page.html").read_text(encoding="utf-8")
|
||||
|
||||
self.assertIn(
|
||||
'body[data-active-view="mentorView"] .header-actions > :is(.global-search-button, .alert-button, .assistant-button, .header-menu-button, .header-date-group, .header-command-group)',
|
||||
shell_styles,
|
||||
)
|
||||
self.assertNotIn('body[data-active-view="mentorView"]', shell_styles)
|
||||
self.assertNotIn("body[data-active-view=mentorView]", shell_styles)
|
||||
self.assertIn('id="mentorPageSubtitle"', mentor_html)
|
||||
self.assertIn('class="mentor-page-header"', mentor_html)
|
||||
self.assertIn("#mentorView .mentor-page-header", self.mentor_styles)
|
||||
for tone in ("violet", "blue", "green", "orange", "red", "teal", "purple", "yellow"):
|
||||
self.assertIn(f".mentor-avatar-tone-{tone} {{", self.mentor_styles, tone)
|
||||
self.assertIn(
|
||||
|
||||
Reference in New Issue
Block a user