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>
83 lines
2.0 KiB
Python
83 lines
2.0 KiB
Python
"""Resolve capture surface from a Win32 window (windows | browser | mobile stub)."""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Any, Optional
|
||
|
||
# Logical surfaces (align with RPA_ELEMENT_LOCATING.md)
|
||
SURFACE_WINDOWS = "windows"
|
||
SURFACE_BROWSER = "browser"
|
||
SURFACE_ANDROID = "android"
|
||
SURFACE_IOS = "ios"
|
||
|
||
_BROWSER_PROCESS_NAMES = {
|
||
"chrome.exe",
|
||
"msedge.exe",
|
||
"chromium.exe",
|
||
"brave.exe",
|
||
"opera.exe",
|
||
"vivaldi.exe",
|
||
}
|
||
|
||
_BROWSER_CLASS_HINTS = {
|
||
"Chrome_WidgetWin_1",
|
||
"Chrome_WidgetWin_0",
|
||
"Chrome_WidgetWin_2",
|
||
}
|
||
|
||
|
||
def normalize_process_name(name: str) -> str:
|
||
return (name or "").strip().lower()
|
||
|
||
|
||
def is_browser_window(
|
||
*,
|
||
process_name: str = "",
|
||
class_name: str = "",
|
||
title: str = "",
|
||
) -> bool:
|
||
proc = normalize_process_name(process_name)
|
||
if proc in _BROWSER_PROCESS_NAMES:
|
||
return True
|
||
cls = (class_name or "").strip()
|
||
if cls in _BROWSER_CLASS_HINTS:
|
||
return True
|
||
# Edge/Chrome sometimes expose empty process briefly; title heuristics last
|
||
t = title or ""
|
||
if " - Google Chrome" in t or " - Microsoft Edge" in t or t.endswith(" - Edge"):
|
||
return True
|
||
if t.endswith(" - Chrome"):
|
||
return True
|
||
return False
|
||
|
||
|
||
def resolve_surface(
|
||
*,
|
||
process_name: str = "",
|
||
class_name: str = "",
|
||
title: str = "",
|
||
) -> str:
|
||
"""Return surface id for this top-level window."""
|
||
if is_browser_window(
|
||
process_name=process_name, class_name=class_name, title=title
|
||
):
|
||
return SURFACE_BROWSER
|
||
return SURFACE_WINDOWS
|
||
|
||
|
||
def surface_from_window_info(info: Any) -> str:
|
||
return resolve_surface(
|
||
process_name=getattr(info, "process_name", "") or "",
|
||
class_name=getattr(info, "class_name", "") or "",
|
||
title=getattr(info, "title", "") or "",
|
||
)
|
||
|
||
|
||
def surface_label(surface: str) -> str:
|
||
return {
|
||
SURFACE_WINDOWS: "桌面 UIA",
|
||
SURFACE_BROWSER: "浏览器 Playwright",
|
||
SURFACE_ANDROID: "Android(未实现)",
|
||
SURFACE_IOS: "iOS(未实现)",
|
||
}.get(surface, surface)
|