diff --git a/frontend/index.html b/frontend/index.html index 2d85bfa..94f4181 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -34,9 +34,9 @@ - + - + @@ -52,8 +52,8 @@ - - + +
diff --git a/frontend/pages/heaven/foundation.css b/frontend/pages/heaven/foundation.css index 0bd40e6..f1f5002 100644 --- a/frontend/pages/heaven/foundation.css +++ b/frontend/pages/heaven/foundation.css @@ -93,28 +93,6 @@ body[data-active-view="heavenView"] .workspace-view { display: none; } -:where(#heavenView) .heaven-tabs { - display: flex; - - border-bottom: 1px solid var(--line); -} - -:where(#heavenView) .heaven-tab { - border-bottom: 3px solid transparent; - - background: transparent; - - cursor: pointer; -} - -:where(#heavenView) .heaven-tab.active { - border-bottom-color: var(--coral); -} - -:where(#heavenView) .heaven-tab:hover { - border-bottom-color: var(--coral); -} - :where(#heavenView) .heaven-panel { display: none; } @@ -1875,60 +1853,6 @@ body[data-active-view="heavenView"] .workspace-view { color: var(--heaven-ink-faint); } -#heavenView .heaven-tab { - font-family: var(--heaven-serif); - - height: 52px; - - position: relative; - - padding: 0px 2px; - - border: 0px; - - color: var(--heaven-ink-soft); - - font-size: 14px; - - font-weight: 600; -} - -#heavenView .heaven-tab::after { - content: ""; - - position: absolute; - - right: 0px; - - bottom: 0px; - - left: 0px; - - height: 2px; - - background: var(--heaven-cinnabar); - - opacity: 0; - - transform: scaleX(0.3); - - transition: opacity 220ms ease, transform 260ms var(--ease-out); -} - -#heavenView .heaven-tab.active { - color: var(--heaven-ink); -} - -#heavenView .heaven-tab:hover { - color: var(--heaven-ink); -} - -#heavenView .heaven-tab.active::after { - opacity: 1; - - transform: scaleX(1); -} - :where(#heavenView) .heaven-proverb { margin: 0px; @@ -1960,7 +1884,7 @@ body[data-active-view="heavenView"] .workspace-view { } #heavenView .button:focus-visible, -#heavenView .heaven-tab:focus-visible, +#heavenView .segment:focus-visible, #heavenView summary:focus-visible { outline: 2px solid var(--heaven-cinnabar); @@ -2623,12 +2547,6 @@ body[data-active-view="heavenView"] .workspace-view { padding: 0px 14px; } - #heavenView .heaven-tabs { - gap: 22px; - - padding: 0px 14px; - } - .heaven-proverb { padding: 8px 14px; @@ -3093,7 +3011,7 @@ body[data-active-view="heavenView"] .workspace-view { #heavenView.heaven-data-loading .heaven-panel, #heavenView.heaven-data-loading .heaven-proverb, -#heavenView.heaven-data-loading .heaven-tabs { +#heavenView.heaven-data-loading .heaven-page-head { opacity: 0.42; pointer-events: none; @@ -3695,7 +3613,7 @@ body[data-active-view="heavenView"] .workspace-view { @media (max-width: 900px) { #heavenView .heaven-panel > , #heavenView .heaven-proverb, -#heavenView .heaven-tabs, +#heavenView .heaven-page-head, #heavenView .heaven-toolbar { width: min(100% - 28px, 1280px); } @@ -3746,22 +3664,6 @@ body[data-active-view="heavenView"] .workspace-view { display: none; } - #heavenView .heaven-tabs { - display: grid; - - grid-template-columns: repeat(3, minmax(0px, 1fr)); - - gap: 0px; - - padding: 0px; - } - - #heavenView .heaven-tab { - width: 100%; - - min-width: 0px; - } - #heavenFortunePanel .fortune-heading { display: flex; @@ -3867,18 +3769,6 @@ body[data-active-view="heavenView"] .workspace-view { } @media (max-width: 520px) { - .heaven-tabs { - gap: 0px; - - padding: 0px 8px; - } - - .heaven-tab { - min-width: 0px; - - flex: 1 1 0%; - } - .heaven-controls { grid-template-columns: 1fr; @@ -6232,26 +6122,6 @@ body[data-active-view="heavenView"] .workspace-view { padding: 12px 20px; } -#heavenView .heaven-tabs { - align-items: stretch; - - gap: 30px; - - border-color: var(--heaven-rule); - - background: rgba(253, 252, 248, 0.96); - - width: min(100% - 40px, 1280px); - - margin-right: auto; - - margin-left: auto; - - min-height: 54px; - - padding: 0px 20px; -} - #heavenView .heaven-proverb { margin-right: auto; @@ -6297,12 +6167,6 @@ body[data-active-view="heavenView"] .workspace-view { padding: 10px 14px; } - #heavenView .heaven-tabs { - min-height: 52px; - - padding: 0px 14px; - } - #heavenView .heaven-proverb { padding: 9px 14px; } @@ -6668,18 +6532,6 @@ body[data-active-view="heavenView"] .workspace-view { color: var(--wt-faint); } -:root[data-theme="light"] #heavenView .wt-tabs .wt-tab { - color: var(--wt-muted); -} - -:root[data-theme="light"] #heavenView .wt-tabs .wt-tab small { - color: var(--wt-faint); -} - -:root[data-theme="light"] #heavenView .wt-tabs .wt-tab.on { - color: var(--wt-gold-bright); -} - :root[data-theme="light"] #heavenView .wt-empty { color: var(--wt-muted); } @@ -7128,88 +6980,6 @@ body[data-active-view="heavenView"] .workspace-view { letter-spacing: 4px; } - .wt-tabs { - display: flex; - - justify-content: center; - - gap: 34px; - - margin-top: 20px; - } - - .wt-tabs .wt-tab { - position: relative; - - padding: 8px 4px; - - border: 0px; - - background: transparent; - - color: rgba(216, 210, 189, 0.5); - - font-size: 15px; - - letter-spacing: 3px; - - transition: color 0.2s; - } - - .wt-tabs .wt-tab small { - display: block; - - margin-top: 3px; - - color: rgba(216, 210, 189, 0.3); - - font-family: inherit; - - font-size: 10px; - - letter-spacing: 1px; - } - - .wt-tabs .wt-tab::after { - content: ""; - - position: absolute; - - bottom: -2px; - - left: 50%; - - width: 0px; - - height: 1.5px; - - background: var(--wt-gold); - - transform: translateX(-50%); - - transition: 0.25s; - } - - .wt-tabs .wt-tab.on { - color: var(--wt-gold-bright); - } - - .wt-tabs .wt-tab.on::after { - width: 100%; - } - - .wt-tabs .wt-tab:disabled { - cursor: default; - } - - .wt-tabs .wt-tab:focus { - outline: none; - } - - .wt-tabs .wt-tab:focus-visible { - box-shadow: rgba(201, 165, 92, 0.45) 0px 2px 0px; - } - .heaven-proverb { margin: 8px 0px 0px; @@ -9072,16 +8842,6 @@ body[data-active-view="heavenView"] .workspace-view { gap: 5px; } - .wt-tabs { - gap: 16px; - } - - .wt-tabs .wt-tab { - font-size: 13px; - - letter-spacing: 2px; - } - .heaven-proverb { text-align: center; } diff --git a/frontend/pages/heaven/page.html b/frontend/pages/heaven/page.html index 6972818..335d317 100644 --- a/frontend/pages/heaven/page.html +++ b/frontend/pages/heaven/page.html @@ -1,16 +1,23 @@遇事不决可问春风,春风不语即随本心
diff --git a/frontend/pages/mentor/foundation.css b/frontend/pages/mentor/foundation.css index e517a5e..355dcec 100644 --- a/frontend/pages/mentor/foundation.css +++ b/frontend/pages/mentor/foundation.css @@ -49,7 +49,7 @@ body[data-active-view="mentorView"] .app-page-context span { height: 100%; min-height: 0; flex-direction: column; - padding: 0; + padding: var(--page-pad-y) 0 0; color: var(--qp-text-1); font-family: "PingFang SC", "Microsoft YaHei", system-ui, sans-serif; } diff --git a/frontend/shared/components/controls.css b/frontend/shared/components/controls.css index 6ccc5d0..fe44fe4 100644 --- a/frontend/shared/components/controls.css +++ b/frontend/shared/components/controls.css @@ -660,7 +660,9 @@ body.sidebar-collapsed .sidebar-collapse-button .lucide { text-align: left; - min-height: 38px; + height: var(--size-statusbar); + + min-height: var(--size-statusbar); display: flex; @@ -670,7 +672,7 @@ body.sidebar-collapsed .sidebar-collapse-button .lucide { margin: auto -8px -8px; - padding: 10px 16px; + padding: 0 16px; border-right: 0px; @@ -691,6 +693,12 @@ body.sidebar-collapsed .sidebar-collapse-button .lucide { font-size: 12px; } +.sidebar-collapse-button .lucide { + width: 14px; + + height: 14px; +} + body.sidebar-collapsed .sidebar-collapse-button span { display: none; } diff --git a/frontend/shared/shell.css b/frontend/shared/shell.css index 0de2241..d76bd79 100644 --- a/frontend/shared/shell.css +++ b/frontend/shared/shell.css @@ -1294,7 +1294,9 @@ body.sidebar-collapsed { } .sidebar-brand { - min-height: 55px; + height: var(--size-topbar); + + min-height: var(--size-topbar); display: flex; @@ -1304,7 +1306,7 @@ body.sidebar-collapsed { margin: 0px -8px 7px; - padding: 0px 16px; + padding: 0 16px; border-bottom: 1px solid var(--r2-line-soft); @@ -2146,13 +2148,17 @@ body.sidebar-collapsed .status-bar { } .module-nav .sidebar-brand { + height: var(--size-topbar); + + min-height: var(--size-topbar); + display: flex; align-items: center; gap: 8px; - padding: 14px 16px; + padding: 0 16px; border-bottom: 1px solid var(--line-soft); } @@ -3494,7 +3500,7 @@ body.sidebar-collapsed .status-bar { flex: 0 0 auto; align-items: center; gap: var(--header-action-gap); - margin-left: 0; + margin-left: auto; overflow: visible; } diff --git a/tests/e2e/app-shell.spec.js b/tests/e2e/app-shell.spec.js index 9ceaa8b..49c927e 100644 --- a/tests/e2e/app-shell.spec.js +++ b/tests/e2e/app-shell.spec.js @@ -3735,3 +3735,99 @@ test("desktop header keeps commands in view and tape text unclipped across works await page.screenshot({ path: path.join(shotDir, "admin-390-night.png") }); fs.writeFileSync(path.join(shotDir, "measurements.json"), `${JSON.stringify(measurements, null, 2)}\n`); }); + +test("HEL-183 heaven tools right-align and shell heights unify", async ({ page }) => { + const fs = require("node:fs"); + const path = require("node:path"); + const shotDir = path.join(__dirname, "../../runtime/hel183-shots"); + fs.mkdirSync(shotDir, { recursive: true }); + await page.setViewportSize({ width: 1440, height: 900 }); + await mockApplication(page, session("admin", true)); + await page.goto("/index.html"); + + const measure = () => page.evaluate(() => { + const box = (node) => { + if (!node) return null; + const r = node.getBoundingClientRect(); + return { x: r.x, y: r.y, right: r.right, width: r.width, height: r.height }; + }; + const brand = document.querySelector(".module-nav .sidebar-brand") || document.querySelector(".sidebar-brand"); + const header = document.querySelector(".app-header"); + const actions = document.querySelector(".header-actions"); + const collapse = document.querySelector(".sidebar-collapse-button"); + const status = document.querySelector(".status-bar"); + const overview = document.querySelector(".overview-strip"); + const brandBox = box(brand); + const headerBox = box(header); + const actionsBox = box(actions); + const collapseBox = box(collapse); + const statusBox = box(status); + return { + brandHeight: brandBox ? Math.round(brandBox.height) : null, + headerHeight: headerBox ? Math.round(headerBox.height) : null, + brandBottom: brandBox ? Math.round(brandBox.y + brandBox.height) : null, + headerBottom: headerBox ? Math.round(headerBox.y + headerBox.height) : null, + collapseHeight: collapseBox ? Math.round(collapseBox.height) : null, + statusHeight: statusBox ? Math.round(statusBox.height) : null, + actionsNearRight: actionsBox && headerBox ? (headerBox.right - actionsBox.right) < 24 : false, + actionsMarginLeft: actions ? getComputedStyle(actions).marginLeft : null, + overviewDisplay: overview ? getComputedStyle(overview).display : null, + mentorPadTop: (() => { + const mentor = document.querySelector("#mentorView"); + return mentor ? getComputedStyle(mentor).paddingTop : null; + })(), + }; + }); + + await page.locator('[data-view="sentimentCycleView"]').first().click(); + await expect(page.locator("#sentimentCycleView")).toHaveClass(/active-view/); + let geo = await measure(); + expect(geo.brandHeight, "logo height").toBe(64); + expect(geo.headerHeight, "header height").toBe(64); + expect(geo.brandBottom, "logo/header bottom align").toBe(geo.headerBottom); + expect(geo.collapseHeight, "collapse height").toBe(28); + expect(geo.statusHeight, "status height").toBe(28); + expect(geo.actionsNearRight, "sentiment tools right").toBe(true); + await page.locator(".app-header").screenshot({ path: path.join(shotDir, "sentiment-header-day.png") }); + await page.screenshot({ path: path.join(shotDir, "sentiment-page-day.png") }); + + await page.locator('[data-view="heavenView"]').first().click(); + await expect(page.locator("#heavenView")).toHaveClass(/active-view/); + await expect(page.locator("#heavenView .heaven-page-head")).toBeVisible(); + await expect(page.locator("#heavenView .heaven-page-head .segment")).toHaveCount(3); + geo = await measure(); + expect(geo.overviewDisplay, "heaven hides overview").toBe("none"); + expect(geo.actionsMarginLeft, "tools margin-left resolved").not.toBe("0px"); + expect(Number.parseFloat(geo.actionsMarginLeft), "tools left auto gap").toBeGreaterThan(40); + expect(geo.actionsNearRight, "heaven tools right").toBe(true); + expect(geo.brandHeight).toBe(64); + expect(geo.collapseHeight).toBe(28); + await page.locator(".app-header").screenshot({ path: path.join(shotDir, "heaven-header-day.png") }); + await page.screenshot({ path: path.join(shotDir, "heaven-page-day.png") }); + + await page.locator('[data-heaven-panel="fortune"]').click(); + await expect(page.locator('[data-heaven-panel="fortune"]')).toHaveClass(/active/); + await expect(page.locator("#heavenFortunePanel")).toHaveClass(/active-heaven-panel/); + + await page.locator('[data-view="mentorView"]').first().click(); + await expect(page.locator("#mentorView")).toHaveClass(/active-view/); + geo = await measure(); + expect(geo.mentorPadTop, "mentor top padding").toBe("14px"); + await page.screenshot({ path: path.join(shotDir, "mentor-page-day.png") }); + + await page.locator("#themeToggle").click(); + await page.locator('[data-view="heavenView"]').first().click(); + await expect(page.locator("#heavenView")).toHaveClass(/active-view/); + geo = await measure(); + expect(geo.actionsNearRight, "heaven night tools right").toBe(true); + expect(geo.brandHeight).toBe(64); + await page.locator(".app-header").screenshot({ path: path.join(shotDir, "heaven-header-night.png") }); + await page.screenshot({ path: path.join(shotDir, "heaven-page-night.png") }); + + await page.locator('[data-view="sentimentCycleView"]').first().click(); + await page.locator(".app-header").screenshot({ path: path.join(shotDir, "sentiment-header-night.png") }); + await page.screenshot({ path: path.join(shotDir, "sentiment-page-night.png") }); + + await page.locator('[data-view="mentorView"]').first().click(); + await page.screenshot({ path: path.join(shotDir, "mentor-page-night.png") }); +});