chore: archive tools/ui_inspector snapshot before migration removal.

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>
This commit is contained in:
2026-08-07 11:19:14 +08:00
parent 46bc573777
commit 633ecff4e2
92 changed files with 13704 additions and 288 deletions

View File

@@ -3,6 +3,9 @@
- 主窗 WeWorkWindow → ``ensure_ui_ready("wecom")``
- 搜索浮层 SearchResultWindow2 → ``ensure_ui_ready("wecom_search")``
未安装 kit 时降级为跳过,不阻断采集。
Warm cache key = profile + hwnd(经 WeCom Adapter),避免主窗 warm
状态错误代表 SearchResultWindow2 已暖。
"""
from __future__ import annotations
@@ -10,9 +13,13 @@ from __future__ import annotations
import time
from typing import Any, Optional
_CACHE_TTL_SEC = 45.0
# A successful provider probe remains valid for the lifetime of a normal
# recording flow. The old 45 second TTL made long desktop flows re-run an
# expensive WeCom wake in the middle of recording and produced minute-late
# evidence.
_CACHE_TTL_SEC = 600.0
# profile -> cache
# cache_key -> cache (profile:hwnd:N)
_caches: dict[str, dict[str, Any]] = {}
_PROFILE_WECOM = "wecom"
@@ -24,8 +31,26 @@ _THRESHOLDS = {
}
def _wecom_adapter():
try:
from adapters.wecom import wecom_adapter
return wecom_adapter
except Exception:
try:
from .adapters.wecom import wecom_adapter # type: ignore
return wecom_adapter
except Exception:
return None
def resolve_wake_profile(*, class_name: str = "", title: str = "") -> Optional[str]:
"""返回应调用的 kit profile id;非企微相关则 None。"""
ad = _wecom_adapter()
if ad is not None:
return ad.wake_profile(class_name=class_name, title=title)
# Compatibility fallback if adapter import fails
cls = (class_name or "").strip()
title = title or ""
if cls == "SearchResultWindow2":
@@ -50,6 +75,13 @@ def kit_available() -> bool:
return False
def _cache_key(profile: str, hwnd: Optional[int]) -> str:
ad = _wecom_adapter()
if ad is not None:
return ad.cache_key(profile=profile, hwnd=int(hwnd or 0))
return f"{profile}:hwnd:{int(hwnd or 0)}"
def auto_ensure_if_wecom(
*,
class_name: str = "",
@@ -68,17 +100,22 @@ def auto_ensure_if_wecom(
"available": kit_available(),
}
warm_min = int(_THRESHOLDS.get(profile, 80))
ad = _wecom_adapter()
warm_min = int(ad.warm_threshold(profile) if ad else _THRESHOLDS.get(profile, 80))
key = _cache_key(profile, hwnd)
cache = _caches.setdefault(
profile, {"t": 0.0, "warm": False, "hwnd": 0, "findall": 0}
key, {"t": 0.0, "warm": False, "hwnd": int(hwnd or 0), "findall": 0, "profile": profile}
)
now = time.monotonic()
hint = int(findall_hint) if findall_hint is not None else -1
# Require same hwnd in cache — main window warm must not cover search popup.
cached_hwnd = int(cache.get("hwnd") or 0)
same_window = (not hwnd) or (cached_hwnd == int(hwnd))
if (
not force
and same_window
and cache.get("warm")
and (now - float(cache.get("t") or 0.0)) < _CACHE_TTL_SEC
and (hint < 0 or hint >= warm_min)
):
return {
"applicable": True,
@@ -91,6 +128,28 @@ def auto_ensure_if_wecom(
"findall": int(cache.get("findall") or 0),
"hwnd": int(cache.get("hwnd") or 0),
"profile": profile,
"cache_key": key,
"observed_findall_hint": hint,
}
# A direct probe is authoritative. Do not run an invasive wake merely
# because this process did not create the cache entry itself.
if not force and hint >= warm_min:
cache.update(
{"t": now, "warm": True, "hwnd": int(hwnd or 0), "findall": hint}
)
return {
"applicable": True,
"available": kit_available(),
"skipped": True,
"reason": "probe_warm",
"ok": True,
"warm": True,
"wake_applied": False,
"findall": hint,
"hwnd": int(hwnd or 0),
"profile": profile,
"cache_key": key,
}
try:
@@ -157,16 +216,49 @@ def auto_ensure_if_wecom(
"profile": profile,
"markers_found": list(after.markers_found) if after is not None else [],
}
report["cache_key"] = key
if result.warm:
cache["t"] = now
cache["warm"] = True
cache["hwnd"] = report["hwnd"]
cache["findall"] = findall
# Persist under result hwnd so main≠search caches stay separate.
final_key = _cache_key(profile, report["hwnd"] or hwnd)
final_cache = _caches.setdefault(
final_key,
{"t": 0.0, "warm": False, "hwnd": int(report["hwnd"] or 0), "findall": 0, "profile": profile},
)
final_cache["t"] = now
final_cache["warm"] = True
final_cache["hwnd"] = int(report["hwnd"] or hwnd or 0)
final_cache["findall"] = findall
report["cache_key"] = final_key
else:
cache["warm"] = False
return report
def note_provider_warm(
*, class_name: str = "", title: str = "", hwnd: int = 0, findall: int = 0
) -> bool:
"""Pin a proven-warm provider so recorder evidence never re-wakes it."""
profile = resolve_wake_profile(class_name=class_name, title=title)
adapter = _wecom_adapter()
threshold = int(
adapter.warm_threshold(profile)
if adapter and profile
else _THRESHOLDS.get(profile or "", 80)
)
count = int(findall or 0)
if not profile or count < threshold:
return False
key = _cache_key(profile, hwnd)
_caches[key] = {
"t": time.monotonic(),
"warm": True,
"hwnd": int(hwnd or 0),
"findall": count,
"profile": profile,
}
return True
def format_kit_wake_line(report: Optional[dict[str, Any]]) -> str:
if not report:
return "kit_wake: (none)"