Keep the last in-template copy on main so history can restore it; product continues in jiangchang-ui-recorder. Co-authored-by: Cursor <cursoragent@cursor.com>
90 lines
2.7 KiB
Python
90 lines
2.7 KiB
Python
"""Decide whether a dump is blank/empty-shell and should not become a flow step.
|
||
|
||
Only applied to browser surface. Does NOT skip same-URL scrolls
|
||
(content may change without URL change).
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Any, Optional
|
||
|
||
import target
|
||
|
||
# Locatable below this counts as "no useful candidates"
|
||
_MIN_LOCATABLE = 3
|
||
# Tree/index tiny enough to treat as empty shell
|
||
_MAX_EMPTY_NODES = 2
|
||
|
||
|
||
def _norm_url(url: str) -> str:
|
||
return (url or "").strip()
|
||
|
||
|
||
def is_blankish_url(url: str) -> bool:
|
||
u = _norm_url(url).lower()
|
||
if not u:
|
||
return True
|
||
if u == "about:blank" or u.startswith("about:blank?"):
|
||
return True
|
||
if u in {"about:srcdoc", "about:newtab"}:
|
||
return True
|
||
if u.startswith("chrome://newtab") or u.startswith("chrome://new-tab-page"):
|
||
return True
|
||
if u.startswith("edge://newtab") or u.startswith("edge://new-tab-page"):
|
||
return True
|
||
return False
|
||
|
||
|
||
def should_skip_empty_dump(
|
||
*,
|
||
surface: str,
|
||
url: str = "",
|
||
locatable_count: Optional[int] = None,
|
||
nodes_kept: Optional[int] = None,
|
||
sparse: bool = False,
|
||
locatable: Optional[list[dict[str, Any]]] = None,
|
||
stats: Optional[dict[str, Any]] = None,
|
||
) -> Optional[str]:
|
||
"""Return skip reason code, or None if the dump should be kept.
|
||
|
||
Safe rules (browser only):
|
||
- blank/newtab URL **and** almost no locatable → skip
|
||
- tiny tree + sparse/blankish + almost no locatable → skip
|
||
|
||
A page with about:blank URL but rich locatable (nav race) is **kept**.
|
||
"""
|
||
if surface != target.SURFACE_BROWSER:
|
||
return None
|
||
|
||
st = stats or {}
|
||
if locatable_count is None:
|
||
if locatable is not None:
|
||
locatable_count = len(locatable)
|
||
else:
|
||
locatable_count = int(st.get("locatable_count") or 0)
|
||
if nodes_kept is None:
|
||
nodes_kept = int(st.get("nodes_kept") or st.get("findall_count") or 0)
|
||
if not sparse:
|
||
sparse = bool(st.get("uia_sparse"))
|
||
|
||
loc_n = int(locatable_count or 0)
|
||
kept = int(nodes_kept or 0)
|
||
blankish = is_blankish_url(url)
|
||
|
||
if blankish and loc_n < _MIN_LOCATABLE:
|
||
return "blank_or_newtab_empty"
|
||
|
||
if loc_n < _MIN_LOCATABLE and kept <= _MAX_EMPTY_NODES and (sparse or blankish):
|
||
return "empty_shell"
|
||
|
||
return None
|
||
|
||
|
||
def skip_reason_text(reason: str, *, url: str = "") -> str:
|
||
u = _norm_url(url) or "(empty)"
|
||
if reason == "blank_or_newtab_empty":
|
||
return f"跳过空页:url={u!r} 且可定位候选过少(blank/新标签页)。"
|
||
if reason == "empty_shell":
|
||
return f"跳过空壳页:url={u!r} 节点过少且 sparse。"
|
||
return f"跳过本拍:{reason} url={u!r}"
|