fix: 修复工作区状态构建明细浮层

This commit is contained in:
Code Queue Review
2026-05-24 09:00:51 +00:00
parent 8321603a63
commit f65dc3f083
6 changed files with 243 additions and 15 deletions
+86 -7
View File
@@ -1140,6 +1140,12 @@ export async function runDevCloudWorkbenchLayoutSmoke(args = {}) {
summary: "#352 default workbench has no aside#resource-explorer, #explorer-resize, resource tree/capability copy, or reclaimed left panel across desktop and mobile.",
observations: summarizeResourceExplorerRemovalCoverage(viewportResults)
},
{
id: "layout-feedback-437-live-build-overlay",
status: allViewportCoverage(viewportResults, "liveBuildOverlayStable") ? "pass" : "blocked",
summary: "#437 workbench status build details open as an overlay without changing the topbar, workspace, or conversation geometry across desktop and mobile.",
observations: summarizeCoverageGap(viewportResults, "liveBuildOverlay")
},
{
id: "layout-left-sidebar-collapse",
status: Object.values(viewportResults).every((result) => result.leftCollapse?.pass) ? "pass" : "blocked",
@@ -1271,7 +1277,7 @@ export async function runDevCloudWorkbenchLayoutSmoke(args = {}) {
summary: useLiveUrl
? "Live browser layout smoke verifies workbench layout, hit targets, overflow, /gate usability, and the two-column wiring panel only; it is not M3 hardware acceptance."
: "Static local browser layout smoke verifies workbench layout, hit targets, overflow, /gate usability, and the two-column wiring panel only; deployment still requires DEV live verification.",
refs: ["pikasTech/HWLAB#273", "pikasTech/HWLAB#276", "pikasTech/HWLAB#278", "pikasTech/HWLAB#287", "pikasTech/HWLAB#288", "pikasTech/HWLAB#352", "pikasTech/HWLAB#99", "pikasTech/HWLAB#227"],
refs: ["pikasTech/HWLAB#273", "pikasTech/HWLAB#276", "pikasTech/HWLAB#278", "pikasTech/HWLAB#287", "pikasTech/HWLAB#288", "pikasTech/HWLAB#352", "pikasTech/HWLAB#437", "pikasTech/HWLAB#239", "pikasTech/HWLAB#99", "pikasTech/HWLAB#227"],
viewports: layoutViewports.map(({ id, width, height }) => ({ id, width, height })),
checks,
blockers,
@@ -3171,22 +3177,26 @@ async function inspectLiveDom(url, options = {}) {
liveBuildListVisibleWhenClosed: visible("#live-build-list"),
liveBuildDetailWhenOpen: (() => {
const details = document.querySelector("#live-build-summary");
const popover = document.querySelector("#live-build-popover");
const list = document.querySelector("#live-build-list");
if (!details || !list) {
if (!details || !popover || !list) {
return {
visible: false,
rows: 0,
scrollContained: false,
overlay: false,
text: "",
overflowX: 0
};
}
details.open = true;
const popoverStyle = getComputedStyle(popover);
const rows = [...list.querySelectorAll(".live-build-row")];
return {
visible: visible("#live-build-list"),
rows: rows.length,
scrollContained: list.clientHeight <= Math.ceil(window.innerHeight * 0.42) + 4,
scrollContained: list.clientHeight <= Math.ceil(window.innerHeight * 0.58) + 4,
overlay: popoverStyle.position === "absolute",
text: list.textContent?.replace(/\s+/gu, " ").trim() ?? "",
overflowX: Math.max(0, ...rows.map((row) => row.scrollWidth - row.clientWidth), list.scrollWidth - list.clientWidth)
};
@@ -7830,36 +7840,103 @@ async function inspectLayoutState(page, { mode, viewport, compareTo }) {
const inspectLiveBuildLayout = async () => {
const details = document.querySelector("#live-build-summary");
const label = document.querySelector("#live-build-latest");
const toggle = document.querySelector("#live-build-toggle");
const popover = document.querySelector("#live-build-popover");
const list = document.querySelector("#live-build-list");
if (!details || !label || !list) {
const topbar = document.querySelector(".topbar");
const workspace = document.querySelector('[data-view="workspace"]');
const conversation = document.querySelector(".conversation-column");
const boxSnapshot = () => ({
topbar: boxForElement(topbar),
workspace: boxForElement(workspace),
conversation: boxForElement(conversation)
});
if (!details || !label || !toggle || !popover || !list || !topbar || !workspace || !conversation) {
return { ok: false, exists: false, summaryOverflowX: null, listOverflowX: null, maxRowOverflowX: null, rows: 0 };
}
const wasOpen = details.open;
const labelBox = boxForElement(label);
const detailsBox = boxForElement(details);
const labelStyle = getComputedStyle(label);
const summaryOverflowX = Math.max(0, label.scrollWidth - label.clientWidth);
const summaryContained =
Boolean(labelBox && detailsBox) &&
labelBox.left >= detailsBox.left - 1 &&
labelBox.right <= detailsBox.right + 1 &&
labelStyle.overflow === "hidden" &&
labelStyle.textOverflow === "ellipsis" &&
labelStyle.whiteSpace === "nowrap";
details.open = false;
await new Promise((resolve) => requestAnimationFrame(resolve));
const before = boxSnapshot();
details.open = true;
await new Promise((resolve) => requestAnimationFrame(resolve));
const rows = [...list.querySelectorAll(".live-build-row")];
const rowOverflows = rows.map((row) => Math.max(0, row.scrollWidth - row.clientWidth));
const maxRowOverflowX = Math.max(0, ...rowOverflows);
const listOverflowX = Math.max(0, list.scrollWidth - list.clientWidth);
const scrollContained = list.clientHeight <= Math.ceil(window.innerHeight * 0.42) + 4;
const popoverBox = boxForElement(popover);
const popoverStyle = getComputedStyle(popover);
const toggleExpanded = toggle.getAttribute("aria-expanded") === "true";
const popoverVisible = popoverBox && popoverBox.width > 0 && popoverBox.height > 0 && popoverStyle.display !== "none";
const popoverViewportContained =
Boolean(popoverBox) &&
popoverBox.left >= -1 &&
popoverBox.right <= window.innerWidth + 1 &&
popoverBox.top >= -1 &&
popoverBox.bottom <= window.innerHeight + 1;
const overlayPositioned = popoverStyle.position === "absolute";
const scrollContained = list.clientHeight <= Math.ceil(window.innerHeight * 0.58) + 4;
const text = list.textContent?.replace(/\s+/gu, " ").trim() ?? "";
const expectedRows = rows.length >= 4;
const expectedText = /构建时间不可用/u.test(text) && /外部镜像或非 HWLAB 构建产物/u.test(text);
const afterOpen = boxSnapshot();
const stableGeometry = ["topbar", "workspace", "conversation"].every((key) => {
const a = before[key];
const b = afterOpen[key];
return a && b && Math.abs(a.top - b.top) <= 1 && Math.abs(a.height - b.height) <= 1;
});
document.querySelector("#live-build-close")?.click();
await new Promise((resolve) => requestAnimationFrame(resolve));
const closedByButton = details.open === false && toggle.getAttribute("aria-expanded") === "false";
details.open = wasOpen;
await new Promise((resolve) => requestAnimationFrame(resolve));
return {
ok: summaryOverflowX <= 1 && listOverflowX <= 1 && maxRowOverflowX <= 1 && scrollContained && expectedRows && expectedText,
ok:
summaryContained &&
listOverflowX <= 1 &&
maxRowOverflowX <= 1 &&
scrollContained &&
expectedRows &&
expectedText &&
overlayPositioned &&
popoverVisible &&
popoverViewportContained &&
toggleExpanded &&
stableGeometry &&
closedByButton,
exists: true,
openStateRestored: details.open === wasOpen,
summaryContained,
summaryOverflowX,
labelBox,
detailsBox,
listOverflowX,
maxRowOverflowX,
rowOverflows,
rows: rows.length,
expectedRows,
expectedText,
overlayPositioned,
popoverVisible,
popoverViewportContained,
toggleExpanded,
scrollContained,
stableGeometry,
closedByButton,
before,
afterOpen,
popoverBox,
textSample: text.slice(0, 240)
};
};
@@ -8231,6 +8308,7 @@ async function inspectLayoutState(page, { mode, viewport, compareTo }) {
rightResizeHandle,
sidePanelScroll,
sidePanelOverflowUsable,
liveBuildOverlayStable: liveBuildLayout.ok,
hardwareTabsCenterHit,
wiring: {
longTableOk: wiringLongTableOk,
@@ -8640,7 +8718,8 @@ function summarizeCoverageGap(viewportResults, issueKey) {
issue288: "issue288SingleTableReady",
issue287: "issue287HardwareTabsReady",
hardwareTabs: "hardwareTabsCenterHit",
wiring: "wiringReadableTwoColumn"
wiring: "wiringReadableTwoColumn",
liveBuildOverlay: "liveBuildOverlayStable"
}[issueKey] ?? "issue287HardwareTabsReady";
return {
status: allViewportCoverage(viewportResults, fieldName) ? "pass" : "skip",