diff --git a/scripts/src/hwlab-node-web-observe-runner-source.ts b/scripts/src/hwlab-node-web-observe-runner-source.ts index 2f60a0ec..76dedfa9 100644 --- a/scripts/src/hwlab-node-web-observe-runner-source.ts +++ b/scripts/src/hwlab-node-web-observe-runner-source.ts @@ -1352,14 +1352,33 @@ async function sampleOnePage(targetPage, { reason, groupSeq, pageRole, targetPag return rect.width > 0 && rect.height > 0 && style.visibility !== "hidden" && style.display !== "none"; }; const textHashInput = (element) => trim(element.textContent || "", 800); - const loadingTextPattern = /加载中|Loading/iu; + const loadingTextPattern = /加载中|\bLoading\b/iu; + const loadingUiPattern = /loading-state|loading-spinner|spinner|progress|skeleton|busy|pending/iu; + const codeLikeSelector = "pre,code,.trace-row-body,.trace-row-markdown,.markdown-body,.message-text,[class*='trace-row' i],[class*='terminal' i],[class*='log' i],[class*='output' i]"; const elementTextForLoading = (element) => [ element.textContent || "", element.getAttribute("aria-label") || "", element.getAttribute("title") || "", element.getAttribute("data-testid") || "" ].map((value) => trim(value, 240)).filter(Boolean).join(" "); - const hasLoadingText = (element) => loadingTextPattern.test(elementTextForLoading(element)); + const elementLooksLikeLoadingUi = (element) => { + const signal = [ + element.getAttribute("class") || "", + element.getAttribute("data-testid") || "", + element.getAttribute("role") || "", + element.getAttribute("aria-busy") || "", + element.getAttribute("aria-label") || "", + element.getAttribute("title") || "" + ].join(" "); + return element.getAttribute("aria-busy") === "true" || element.getAttribute("role") === "status" || loadingUiPattern.test(signal); + }; + const elementIsCodeLike = (element) => Boolean(element.closest(codeLikeSelector)) && !elementLooksLikeLoadingUi(element); + const hasLoadingText = (element) => { + const text = elementTextForLoading(element); + if (!loadingTextPattern.test(text)) return false; + if (elementIsCodeLike(element)) return false; + return true; + }; const elementDescriptor = (element) => { if (!element) return null; const className = String(element.className || "").replace(/\s+/g, " ").trim().split(" ").slice(0, 6).join(" ");