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:
MS-01-Codex
2026-08-18 17:19:03 +08:00
co-authored by multica-agent
parent 8fa52b43a8
commit 9f8fbc1937
9 changed files with 125 additions and 116 deletions
+39 -6
View File
@@ -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));
+5 -4
View File
@@ -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(