fix 前端优化
This commit is contained in:
+113
-100
@@ -1,156 +1,169 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
function readThemeCss() {
|
||||
return readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
}
|
||||
|
||||
function compact(value) {
|
||||
return value.replace(/\s+/g, "");
|
||||
}
|
||||
|
||||
describe("platform web shared theme CSS", () => {
|
||||
it("keeps production lifecycle and alert controls bounded at narrow width", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const narrow = themeCss.slice(themeCss.indexOf("@media (max-width: 640px)", themeCss.indexOf(".production-governance-panel")));
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(narrow).toContain(".plugin-lifecycle-controls");
|
||||
expect(narrow).toContain("grid-template-columns: minmax(0, 1fr)");
|
||||
expect(narrow).toContain(".production-alert-actions button");
|
||||
expect(themeCss).toContain("overflow-wrap: anywhere");
|
||||
expect(css).toContain("@media(max-width:640px){.plugin-lifecycle-controls{grid-template-columns:minmax(0,1fr)}");
|
||||
expect(css).toContain(".production-alert-actions,.production-alert-actionsbutton{width:100%}");
|
||||
expect(css).toContain("overflow-wrap:anywhere");
|
||||
});
|
||||
|
||||
it("suppresses nested state frames inside shared framed surfaces", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const nestedStateReset = themeCss.slice(themeCss.indexOf("A state view inside an already framed surface"));
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(nestedStateReset).toContain(".state-view::after");
|
||||
expect(nestedStateReset).toContain("content: none");
|
||||
expect(nestedStateReset).toContain("background: transparent");
|
||||
expect(css).toContain(".state-view::after,:root[data-theme-palette]:where(");
|
||||
expect(css).toContain(".state-view::before{content:none;background:00;box-shadow:none;filter:none}");
|
||||
});
|
||||
|
||||
it("keeps server card runtime actions as compact overlay menus", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const popoverRule = themeCss.slice(themeCss.indexOf(".runtime-action-popover"), themeCss.indexOf("/* ---- catalog / detail lists ---- */"));
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(popoverRule).toContain("position: fixed");
|
||||
expect(popoverRule).toContain("z-index: 45");
|
||||
expect(popoverRule).toContain(".runtime-action-grid");
|
||||
expect(popoverRule).toContain("grid-template-columns: repeat(2, minmax(0, 1fr))");
|
||||
expect(css).toContain(".runtime-action-popover{position:fixed;z-index:45");
|
||||
expect(css).toContain(".runtime-action-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))");
|
||||
});
|
||||
|
||||
it("keeps server card stat tiles readable over busy backgrounds", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const statRule = themeCss.slice(themeCss.indexOf(".server-card-stat {"), themeCss.indexOf(".server-card-meters"));
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(statRule).toContain("border: 1px solid");
|
||||
expect(statRule).toContain("--surface-solid");
|
||||
expect(statRule).toContain("font-weight: 850");
|
||||
expect(themeCss).toContain(':root[data-custom-background="true"] .server-card-stat');
|
||||
expect(css).toContain(".server-card-stat{display:grid;gap:2px;padding:8px;border:1pxsolid");
|
||||
expect(css).toContain("--surface-solid");
|
||||
expect(css).toContain("font-weight:850");
|
||||
expect(css).toContain(":root[data-custom-background=true].server-card-stat");
|
||||
});
|
||||
|
||||
it("keeps magical custom-background signal rows readable without the jelly wash", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const signalRule = themeCss.slice(
|
||||
themeCss.indexOf(':root[data-custom-background="true"][data-theme-palette="magical-girl"] .signal-item'),
|
||||
themeCss.indexOf(':root[data-custom-background="true"][data-theme-palette="magical-girl"] .signal-item strong')
|
||||
);
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(signalRule).toContain("rgba(58, 44, 56, 0.72)");
|
||||
expect(signalRule).toContain("backdrop-filter: blur(12px) saturate(0.92)");
|
||||
expect(signalRule).not.toContain("var(--jelly-highlight), var(--glass-wash), var(--surface-solid)");
|
||||
expect(css).toContain(":root[data-custom-background=true][data-theme-palette=magical-girl].signal-item");
|
||||
expect(css).toContain("rgba(58,44,56,.72)");
|
||||
expect(css).toContain("backdrop-filter:blur(12px)saturate(0.92)");
|
||||
expect(css).not.toContain("var(--jelly-highlight),var(--glass-wash),var(--surface-solid)");
|
||||
});
|
||||
|
||||
it("keeps shared controls off the heavy jelly and corner-sparkle button wash", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const primaryCommandRule = themeCss.slice(themeCss.indexOf(".primary-command {"), themeCss.indexOf(".action-strip .primary-command"));
|
||||
const css = readThemeCss();
|
||||
|
||||
expect(themeCss).toContain("--control-surface");
|
||||
expect(themeCss).toContain("--primary-command-surface");
|
||||
expect(themeCss).not.toContain("background: var(--jelly-highlight), var(--glass-wash), var(--surface-solid)");
|
||||
expect(themeCss).not.toContain("linear-gradient(145deg, rgba(255, 255, 255, 0.72), transparent 36%)");
|
||||
expect(primaryCommandRule).not.toContain("background-position: right 8px top 4px, center, center");
|
||||
expect(css).toContain("--control-surface");
|
||||
expect(css).toContain("--primary-command-surface");
|
||||
expect(css).not.toContain("background: var(--jelly-highlight), var(--glass-wash), var(--surface-solid)");
|
||||
expect(css).not.toContain("linear-gradient(145deg, rgba(255, 255, 255, 0.72), transparent 36%)");
|
||||
});
|
||||
|
||||
it("uses shared orbital construct backdrop and button ring effects", () => {
|
||||
const themeCss = readThemeCss();
|
||||
const constructRule = themeCss.slice(themeCss.indexOf(".global-orbital-construct"), themeCss.indexOf(".mobile-sidebar-backdrop"));
|
||||
|
||||
expect(constructRule).toContain("clip-path:circle(49% at 50% 50%)");
|
||||
expect(constructRule).toContain("background:var(--orbital-construct-image) center/contain no-repeat transparent");
|
||||
expect(constructRule).toContain("filter:var(--orbital-construct-filter)");
|
||||
expect(constructRule).toContain(".global-orbital-construct::before,.global-orbital-construct::after{content:none}");
|
||||
expect(constructRule).toContain(":root[data-theme-palette=mecha-black] .global-orbital-construct{display:block;opacity:.22}");
|
||||
expect(constructRule).toContain(":root[data-theme-palette=magical-girl] .global-orbital-construct{display:block;opacity:.24}");
|
||||
expect(constructRule).toContain(".global-particle-lightfall");
|
||||
expect(constructRule).toContain(".global-particle-side-ray");
|
||||
expect(themeCss).not.toContain("global-particle-frame");
|
||||
expect(themeCss).not.toContain("global-particle-orbit");
|
||||
expect(themeCss).not.toContain("particle-orbit-drift");
|
||||
expect(constructRule).toContain("orbital-construct-drift");
|
||||
expect(constructRule).toContain("prefers-reduced-motion");
|
||||
expect(constructRule).toContain("var(--command-ring-sheen");
|
||||
expect(constructRule).toContain(".app-nav-group-button::after");
|
||||
expect(constructRule).toContain("magic-side-ray-pulse-right");
|
||||
expect(constructRule).toContain("transform:translateY(1px)");
|
||||
expect(themeCss).toContain(":root[data-custom-background=true] .global-particle-layer{opacity:.42}");
|
||||
expect(themeCss).toContain(":root[data-custom-background=true][data-theme-palette=magical-girl] .global-particle-layer{opacity:.54}");
|
||||
expect(themeCss).not.toContain(":root[data-custom-background=true] .global-particle-layer{opacity:.03}");
|
||||
});
|
||||
|
||||
it("keeps magical-girl custom-background panel overlays restrained", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const customMagicalRule = themeCss.slice(
|
||||
themeCss.indexOf(':root[data-custom-background="true"][data-theme-palette="magical-girl"] {'),
|
||||
themeCss.indexOf(':root[data-custom-background="true"][data-theme-palette="magical-girl"] .app-sidebar')
|
||||
);
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(customMagicalRule).toContain("--frame-accessory-opacity: 0.22");
|
||||
expect(customMagicalRule).toContain("rgba(22, 22, 29, 0.64)");
|
||||
expect(customMagicalRule).not.toContain("255, 119, 200, 0.78");
|
||||
expect(css).toContain(":root[data-custom-background=true][data-theme-palette=magical-girl]{--frame-accessory-opacity:0.22");
|
||||
expect(css).toContain("--surface:rgba(22,22,29,0.42)");
|
||||
expect(css).toContain("linear-gradient(180deg,rgba(24,24,31,.22),rgba(18,19,26,.18))");
|
||||
expect(css).not.toContain("255,119,200,0.78");
|
||||
});
|
||||
|
||||
it("keeps catalog and console panels translucent without fading content", () => {
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(css).toContain(".catalog-card,.console-panel{border:1pxsolidvar(--line);border-radius:8px;background:var(--sugar-dust),linear-gradient(135deg,color-mix(insrgb,var(--surface)22%,transparent),color-mix(insrgb,var(--surface-raised)14%,transparent))");
|
||||
expect(css).toContain("-webkit-backdrop-filter:blur(10px)saturate(.9);backdrop-filter:blur(10px)saturate(.9)");
|
||||
expect(css).toContain(":root[data-custom-background=true].catalog-card");
|
||||
expect(css).toContain("linear-gradient(180deg,rgba(5,10,16,.42),rgba(5,10,16,.34))");
|
||||
expect(css).not.toContain(".catalog-card,.console-panel{opacity:");
|
||||
expect(css).not.toContain(".catalog-card,.console-panel{border:1pxsolidvar(--line);border-radius:8px;background:var(--panel-material);backdrop-filter:blur(22px)saturate(1.28)");
|
||||
});
|
||||
|
||||
it("styles runtime task progress as a dialog with staged status", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const progressRule = themeCss.slice(themeCss.indexOf(".runtime-task-backdrop"), themeCss.indexOf("/* ---- narrow screens ---- */"));
|
||||
const css = readThemeCss();
|
||||
|
||||
expect(progressRule).toContain(".runtime-task-panel");
|
||||
expect(progressRule).toContain(".runtime-task-meter-track");
|
||||
expect(progressRule).toContain(".runtime-task-stages");
|
||||
expect(progressRule).toContain(".runtime-task-log");
|
||||
expect(progressRule).toContain("prefers-reduced-motion");
|
||||
expect(css).toContain(".runtime-task-panel");
|
||||
expect(css).toContain(".runtime-task-meter-track");
|
||||
expect(css).toContain(".runtime-task-stages");
|
||||
expect(css).toContain(".runtime-task-log");
|
||||
expect(css).toContain("prefers-reduced-motion");
|
||||
});
|
||||
|
||||
it("styles operations surfaces with shared theme materials and narrow-screen collapse", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const operationsRule = themeCss.slice(themeCss.indexOf("/* ---- operations console enrichment ---- */"));
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(operationsRule).toContain("var(--panel-material)");
|
||||
expect(operationsRule).toContain(".operations-tray-panel");
|
||||
expect(operationsRule).toContain("@media (max-width: 760px)");
|
||||
expect(operationsRule).toContain("grid-template-columns: 1fr");
|
||||
expect(operationsRule).toContain("prefers-reduced-motion");
|
||||
expect(operationsRule).toContain(".operations-job-row-failed");
|
||||
expect(css).toContain(".operations-tray{position:relative;z-index:2");
|
||||
expect(css).toContain("background:var(--panel-material),var(--surface)");
|
||||
expect(css).toContain(".operations-tray-panel{position:absolute");
|
||||
expect(css).toContain("@media(max-width:760px){.operations-command-grid{grid-template-columns:1fr}");
|
||||
expect(css).toContain(".operations-job-row-failed");
|
||||
expect(css).toContain("prefers-reduced-motion");
|
||||
});
|
||||
|
||||
it("keeps mobile navigation off-canvas with readable vertical labels", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const mobileRule = themeCss.slice(themeCss.indexOf("/* ---- narrow screens ---- */"), themeCss.indexOf("/* A state view inside an already framed surface"));
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(mobileRule).toContain(".mobile-sidebar-handle");
|
||||
expect(mobileRule).toContain("position: fixed");
|
||||
expect(mobileRule).toContain("padding: 14px 14px 14px 52px");
|
||||
expect(mobileRule).toContain("transform: translateX(calc(-100% - 18px))");
|
||||
expect(mobileRule).toContain(".app-sidebar-mobile-open");
|
||||
expect(mobileRule).toContain("grid-template-columns: 40px minmax(0, 1fr)");
|
||||
expect(mobileRule).toContain(".app-nav-copy strong");
|
||||
expect(mobileRule).toContain("display: block");
|
||||
expect(css).toContain(".mobile-sidebar-handle{position:fixed");
|
||||
expect(css).toContain(".app-sidebar{position:fixed;inset:0auto00");
|
||||
expect(css).toContain("transform:translateX(calc(-100%-18px))");
|
||||
expect(css).toContain(".app-sidebar-mobile-open");
|
||||
expect(css).toContain("grid-template-columns:40pxminmax(0,1fr)");
|
||||
expect(css).toContain(".app-nav-copystrong{display:block");
|
||||
});
|
||||
|
||||
it("keeps desktop sidebar controls pinned inside the viewport", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const shellRule = themeCss.slice(themeCss.indexOf(".app-shell {"), themeCss.indexOf(".workspace-background-layer"));
|
||||
const sidebarRule = themeCss.slice(themeCss.indexOf(".app-sidebar {"), themeCss.indexOf(".app-brand {"));
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(shellRule).toContain("min-height: 100dvh");
|
||||
expect(sidebarRule).toContain("position: sticky");
|
||||
expect(sidebarRule).toContain("height: 100dvh");
|
||||
expect(sidebarRule).toContain("overflow-y: auto");
|
||||
expect(sidebarRule).toContain("overscroll-behavior: contain");
|
||||
expect(css).toContain(".app-shell{min-height:100dvh");
|
||||
expect(css).toContain(".app-sidebar{position:sticky");
|
||||
expect(css).toContain("height:100dvh");
|
||||
expect(css).toContain("overflow-y:auto");
|
||||
expect(css).toContain("overscroll-behavior:contain");
|
||||
});
|
||||
|
||||
it("keeps dialogs and drawers internally scrollable on short viewports", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const dialogRule = themeCss.slice(themeCss.indexOf("/* ---- drawer ---- */"), themeCss.indexOf(".plugin-detail-actions"));
|
||||
const confirmStart = themeCss.indexOf("/* ---- confirm dialog ---- */");
|
||||
const confirmRule = themeCss.slice(confirmStart, themeCss.indexOf(".confirm-actions {", confirmStart));
|
||||
const mobileRule = themeCss.slice(themeCss.indexOf("/* ---- narrow screens ---- */"), themeCss.indexOf("/* A state view inside an already framed surface"));
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(dialogRule).toContain(".dialog-panel-header");
|
||||
expect(dialogRule).toContain(".drawer-panel > .panel-header");
|
||||
expect(dialogRule).toContain("position: sticky");
|
||||
expect(dialogRule).toContain("overflow-y: auto");
|
||||
expect(dialogRule).toContain("overscroll-behavior: contain");
|
||||
expect(confirmRule).toContain("max-height: calc(100dvh - 32px)");
|
||||
expect(confirmRule).toContain("overflow-y: auto");
|
||||
expect(mobileRule).toContain("max-height: calc(100dvh - 20px)");
|
||||
expect(css).toContain(".dialog-panel-header,.drawer-panel>.panel-header{position:sticky");
|
||||
expect(css).toContain("overflow-y:auto");
|
||||
expect(css).toContain("overscroll-behavior:contain");
|
||||
expect(css).toContain("max-height:calc(100dvh-32px)");
|
||||
expect(css).toContain("max-height:calc(100dvh-20px)");
|
||||
});
|
||||
|
||||
it("keeps magical card decoration from becoming square background patches", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const magicalSurfaceRule = themeCss.slice(
|
||||
themeCss.indexOf(':root[data-theme-palette="magical-girl"] .metric-card'),
|
||||
themeCss.indexOf(':root[data-theme-palette="magical-girl"] :where(')
|
||||
);
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(magicalSurfaceRule).toContain("background: var(--panel-material)");
|
||||
expect(magicalSurfaceRule).not.toContain("background-size: 56px 56px");
|
||||
expect(magicalSurfaceRule).not.toContain("background-image: var(--corner-sparkle), var(--panel-material)");
|
||||
expect(css).toContain(":root[data-theme-palette=magical-girl].catalog-card");
|
||||
expect(css).toContain("background:var(--sugar-dust),linear-gradient(180deg,color-mix(insrgb,var(--surface)16%,transparent),color-mix(insrgb,var(--surface-raised)10%,transparent));overflow:visible");
|
||||
expect(css).not.toContain("background-size:56px56px");
|
||||
expect(css).not.toContain("background-image:var(--corner-sparkle),var(--panel-material)");
|
||||
expect(css).not.toContain(".global-magic-ring");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user