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

@@ -0,0 +1,29 @@
"""Surface drivers for UI Inspector (windows / browser / mobile stub)."""
from __future__ import annotations
from .base import DumpResult, SurfaceDriver
from .browser_pw import BrowserPlaywrightDriver
from .mobile import MobileDriver
from .windows_uia import WindowsUiaDriver
import target as _target
def get_driver(surface: str) -> SurfaceDriver:
if surface == _target.SURFACE_WINDOWS:
return WindowsUiaDriver()
if surface == _target.SURFACE_BROWSER:
return BrowserPlaywrightDriver()
if surface in {_target.SURFACE_ANDROID, _target.SURFACE_IOS}:
return MobileDriver(surface=surface)
raise RuntimeError(f"ERROR:SURFACE_UNSUPPORTED unknown surface={surface!r}")
__all__ = [
"DumpResult",
"SurfaceDriver",
"WindowsUiaDriver",
"BrowserPlaywrightDriver",
"MobileDriver",
"get_driver",
]

View File

@@ -0,0 +1,38 @@
"""Driver protocol: dump one step's UI tree for a surface."""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any, Optional, Protocol
@dataclass
class DumpResult:
surface: str
driver: str
window: dict[str, Any]
tree: dict[str, Any]
index: list[dict[str, Any]]
locatable: list[dict[str, Any]]
stats: dict[str, Any]
selection: dict[str, Any] = field(default_factory=dict)
url: str = ""
page_title: str = ""
locatable_text: str = ""
tree_text: str = ""
screenshot_bytes: Optional[bytes] = None # if set, artifacts write screen.png from bytes
use_hwnd_screenshot: bool = True # windows: ImageGrab by hwnd
class SurfaceDriver(Protocol):
def dump(
self,
*,
info: Any,
selection_meta: dict[str, Any],
max_depth: int = 16,
max_nodes: int = 8000,
warmup_mode: str = "full",
merge_same_pid_if_sparse: bool = True,
warmed_pids: Optional[set[int]] = None,
) -> DumpResult: ...

View File

@@ -0,0 +1,90 @@
"""Browser whole-page dump via Inspector Playwright session + kit page_dump."""
from __future__ import annotations
from typing import Any, Optional
import kit_browser
import target
from .base import DumpResult
class BrowserPlaywrightDriver:
surface = target.SURFACE_BROWSER
driver_name = "playwright"
def dump(
self,
*,
info: Any,
selection_meta: dict[str, Any],
max_depth: int = 16,
max_nodes: int = 8000,
warmup_mode: str = "full",
merge_same_pid_if_sparse: bool = True,
warmed_pids: Optional[set[int]] = None,
page_id: str = "",
document_id: str = "",
frame_id: str = "",
) -> DumpResult:
del max_depth, warmup_mode, merge_same_pid_if_sparse, warmed_pids # unused
sess = kit_browser.ensure_session()
dump = kit_browser.dump_current_page(
max_nodes=max_nodes, locatable_limit=300, page_id=page_id
)
try:
png = kit_browser.screenshot_png(page_id=page_id)
except Exception:
png = None
from jiangchang_skill_core.rpa.page_dump import (
format_locatable_text,
format_tree_text,
)
url = dump.get("url") or ""
page_title = dump.get("title") or ""
window = info.to_dict() if hasattr(info, "to_dict") else dict(info or {})
# Prefer live page identity for labeling / meta
window = {
**window,
"title": page_title or window.get("title") or "",
"class_name": window.get("class_name") or "PlaywrightPage",
"process_name": window.get("process_name") or "chrome.exe",
"url": url,
}
stats = dict(dump.get("stats") or {})
active_page = dict(stats.get("active_page") or {})
stats["browser_session"] = {
"profile_dir": sess.get("profile_dir"),
"reused": sess.get("reused"),
"page_count": active_page.get("page_count") or sess.get("page_count"),
"resolve": active_page.get("resolve") or "visible",
"note": (
"dump 来自 Inspector 自管 Playwright 的当前可见标签页"
"(document.visibilityState),不是系统 Chrome 的 UIA,"
"也不是固定的 pages[0]。多标签时请切到目标页再拾取。"
),
}
sel = dict(selection_meta or {})
sel["surface"] = self.surface
sel["browser_url"] = url
sel["page_id"] = page_id or active_page.get("page_id") or ""
sel["document_id"] = document_id
sel["frame_id"] = frame_id
return DumpResult(
surface=self.surface,
driver=self.driver_name,
window=window,
tree=dump.get("tree") or {},
index=list(dump.get("index") or []),
locatable=list(dump.get("locatable") or []),
stats=stats,
selection=sel,
url=url,
page_title=page_title,
locatable_text=format_locatable_text(dump.get("locatable") or []),
tree_text=format_tree_text(dump.get("tree") or {}),
screenshot_bytes=png,
use_hwnd_screenshot=False,
)

View File

@@ -0,0 +1,30 @@
"""Mobile surface stub — reserved for future android/ios dump."""
from __future__ import annotations
from typing import Any, Optional
import target
from .base import DumpResult
class MobileDriver:
def __init__(self, *, surface: str = target.SURFACE_ANDROID) -> None:
self.surface = surface
self.driver_name = "mobile_stub"
def dump(
self,
*,
info: Any,
selection_meta: dict[str, Any],
max_depth: int = 16,
max_nodes: int = 8000,
warmup_mode: str = "full",
merge_same_pid_if_sparse: bool = True,
warmed_pids: Optional[set[int]] = None,
) -> DumpResult:
raise RuntimeError(
f"ERROR:SURFACE_UNSUPPORTED 手机端拾取尚未实现(surface={self.surface})。"
"请先用桌面或浏览器目标。"
)

View File

@@ -0,0 +1,58 @@
"""Windows desktop dump via existing desktop_uia."""
from __future__ import annotations
from typing import Any, Optional
import desktop_uia as uia
import target
from .base import DumpResult
class WindowsUiaDriver:
surface = target.SURFACE_WINDOWS
driver_name = "uiautomation"
def dump(
self,
*,
info: Any,
selection_meta: dict[str, Any],
max_depth: int = 16,
max_nodes: int = 8000,
warmup_mode: str = "full",
merge_same_pid_if_sparse: bool = True,
warmed_pids: Optional[set[object]] = None,
) -> DumpResult:
warmed = warmed_pids if warmed_pids is not None else set()
mode = warmup_mode
warm_key = (int(info.pid), str(info.class_name or ""))
if (warm_key in warmed or int(info.pid) in warmed) and mode == "full":
mode = "light"
merge_sparse = merge_same_pid_if_sparse
if (info.class_name or "") == "SearchResultWindow2":
merge_sparse = False
tree, index, stats, locatable = uia.dump_window_tree(
info.hwnd,
max_depth=max_depth,
max_nodes=max_nodes,
only_visible=False,
warmup_mode=mode,
merge_same_pid_if_sparse=merge_sparse,
)
return DumpResult(
surface=self.surface,
driver=self.driver_name,
window=info.to_dict(),
tree=tree,
index=index,
locatable=locatable,
stats=stats,
selection=dict(selection_meta or {}),
url="",
page_title="",
locatable_text=uia.format_locatable_text(locatable),
tree_text=uia.format_tree_text(tree),
screenshot_bytes=None,
use_hwnd_screenshot=True,
)