From c75eaa701b12e7c5c709663edaed81af12e45afc Mon Sep 17 00:00:00 2001 From: chendelian <116870791@qq.com> Date: Sun, 2 Aug 2026 11:59:49 +0800 Subject: [PATCH] feat(desktop-wake): add wecom_search profile for SearchResultWindow2 (1.2.6) Co-authored-by: Cursor --- pyproject.toml | 2 +- src/jiangchang_desktop_wake/ACCEPTANCE.md | 32 +++++++++++++++++-- src/jiangchang_desktop_wake/README.md | 15 ++++++++- src/jiangchang_desktop_wake/profiles.py | 11 +++++++ .../profiles/wecom_search.json | 14 ++++++++ src/jiangchang_desktop_wake/types.py | 6 ++++ src/jiangchang_desktop_wake/wake.py | 5 +-- tests/desktop_wake/test_profiles_and_types.py | 12 +++++++ 8 files changed, 90 insertions(+), 7 deletions(-) create mode 100644 src/jiangchang_desktop_wake/profiles/wecom_search.json diff --git a/pyproject.toml b/pyproject.toml index 22cef46..536cea9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "jiangchang-platform-kit" -version = "1.2.5" +version = "1.2.6" description = "匠厂平台共享组件:Skill 实体 SDK + 桌面应用自动化 SDK + 桌面 UIA 暖树" readme = "README.md" requires-python = ">=3.10" diff --git a/src/jiangchang_desktop_wake/ACCEPTANCE.md b/src/jiangchang_desktop_wake/ACCEPTANCE.md index e048937..129b6d0 100644 --- a/src/jiangchang_desktop_wake/ACCEPTANCE.md +++ b/src/jiangchang_desktop_wake/ACCEPTANCE.md @@ -1,9 +1,14 @@ -# jiangchang_desktop_wake 验收(ensure 主路径,1.2.5+) +# jiangchang_desktop_wake 验收(ensure 主路径,1.2.6+) 正式对外路径是 **`ensure`**(内部:`activate` → `desktop-wake --mode n2-wake` → `probe`)。 `n2-wake`:**仅 OBJID_CLIENT** 走 DuiLib;不做进程内 FindAll、不同步 GETOBJECT、不 SPI 读屏;判暖只看进程外 probe。 `c2` / `inject` 仅诊断用。 +| profile | 窗 | 1.2.6 | +|---|---|---| +| `wecom` | `WeWorkWindow` | 主窗(原有) | +| `wecom_search` | `SearchResultWindow2` | 搜索联系人浮层;不 Maximize;markers=`search_result`,`user_item` | + ## 0. 编译(企微先完全退出,含托盘) ```powershell @@ -72,7 +77,28 @@ python -m jiangchang_desktop_wake probe --profile wecom --- -## C. 单测(不依赖企微窗) +## C. 搜索浮层 wecom_search(1.2.6+) + +1. 打开企微主窗并登录;在搜索框输入联系人,**保持搜索结果浮层打开**(标题/类名 `SearchResultWindow2`)。 +2. 验收: + +```powershell +cd D:\OpenClaw\client-jiangchang\jiangchang-platform-kit +$env:PYTHONPATH = ".\src" +python -m jiangchang_desktop_wake probe --profile wecom_search +python -m jiangchang_desktop_wake ensure --profile wecom_search +python -m jiangchang_desktop_wake probe --profile wecom_search +``` + +| 期望 | 说明 | +|---|---| +| ensure 退出码 `0` | 浮层已暖 | +| `warm=true` | markers 含 `search_result` / `user_item`(或 findall 足够) | +| `WINDOW_NOT_FOUND` | 浮层未打开——先展开搜索结果再测 | + +--- + +## D. 单测(不依赖企微窗) ```powershell cd D:\OpenClaw\client-jiangchang\jiangchang-platform-kit @@ -82,7 +108,7 @@ python -m unittest discover -s tests/desktop_wake -v --- -## D. 诊断命令(非主路径) +## E. 诊断命令(非主路径) ```powershell python -m jiangchang_desktop_wake inject --profile wecom diff --git a/src/jiangchang_desktop_wake/README.md b/src/jiangchang_desktop_wake/README.md index 20a6717..6be9193 100644 --- a/src/jiangchang_desktop_wake/README.md +++ b/src/jiangchang_desktop_wake/README.md @@ -22,15 +22,28 @@ pip install uiautomation # 正式 API(技能 / 拾取应调这个) python -m jiangchang_desktop_wake ensure --profile wecom + +# 搜索结果浮层(先在企微搜索框输入并展开联系人列表) +python -m jiangchang_desktop_wake ensure --profile wecom_search +python -m jiangchang_desktop_wake probe --profile wecom_search ``` ```python from jiangchang_desktop_wake import ensure_ui_ready, probe -r = ensure_ui_ready("wecom") # 仍冷则抛 DesktopWakeError +r = ensure_ui_ready("wecom") # 主窗;仍冷则抛 DesktopWakeError print(r.warm, r.probe_after.findall_count) + +# 搜索浮层 SearchResultWindow2(需已打开搜索结果) +rs = ensure_ui_ready("wecom_search") +print(rs.warm, rs.probe_after.findall_count, rs.probe_after.markers_found) ``` +| profile | 窗 | 说明 | +|---|---|---| +| `wecom` | `WeWorkWindow` | 主窗;markers=`con_op`,`searchedit` | +| `wecom_search` | `SearchResultWindow2` | 搜索联系人浮层;markers=`search_result`,`user_item`;不 Maximize | + | CLI | 退出码 | |---|---| | `ensure` | `0` 已暖;非 0 仍冷或异常 | diff --git a/src/jiangchang_desktop_wake/profiles.py b/src/jiangchang_desktop_wake/profiles.py index 5694ea6..3f255be 100644 --- a/src/jiangchang_desktop_wake/profiles.py +++ b/src/jiangchang_desktop_wake/profiles.py @@ -42,6 +42,17 @@ _BUILTIN = { "native_helper": "desktop-wake.exe", "native_args": ["wecom", "0x{hwnd_hex}", "--mode", "n1"], }, + "wecom_search": { + "id": "wecom_search", + "description": "企业微信搜索结果浮层 SearchResultWindow2", + "window_class": "SearchResultWindow2", + "warm_min_findall": 20, + "warm_markers": ["search_result", "user_item"], + "activate_maximize": False, + "wake_strategies": ["activate", "native_helper"], + "native_helper": "desktop-wake.exe", + "native_args": ["wecom", "0x{hwnd_hex}", "--mode", "n2-wake"], + }, } diff --git a/src/jiangchang_desktop_wake/profiles/wecom_search.json b/src/jiangchang_desktop_wake/profiles/wecom_search.json new file mode 100644 index 0000000..b81b3ce --- /dev/null +++ b/src/jiangchang_desktop_wake/profiles/wecom_search.json @@ -0,0 +1,14 @@ +{ + "id": "wecom_search", + "description": "企业微信搜索结果浮层 SearchResultWindow2(联系人列表 user_item)", + "window_class": "SearchResultWindow2", + "warm_min_findall": 20, + "warm_markers": ["search_result", "user_item"], + "activate_maximize": false, + "wake_strategies": [ + "activate", + "native_helper" + ], + "native_helper": "desktop-wake.exe", + "native_args": ["wecom", "0x{hwnd_hex}", "--mode", "n2-wake"] +} diff --git a/src/jiangchang_desktop_wake/types.py b/src/jiangchang_desktop_wake/types.py index 1326f4b..88d5a3a 100644 --- a/src/jiangchang_desktop_wake/types.py +++ b/src/jiangchang_desktop_wake/types.py @@ -27,9 +27,14 @@ class Profile: ) native_helper: Optional[str] = None native_args: list[str] = field(default_factory=list) + # 浮层窗(如搜索结果)禁止 Maximize,避免打乱搜索态 + activate_maximize: bool = True @classmethod def from_dict(cls, data: dict[str, Any]) -> "Profile": + maximize = data.get("activate_maximize") + if maximize is None: + maximize = True return cls( id=str(data.get("id") or "unknown"), description=str(data.get("description") or ""), @@ -49,6 +54,7 @@ class Profile: ), native_helper=data.get("native_helper"), native_args=list(data.get("native_args") or []), + activate_maximize=bool(maximize), ) diff --git a/src/jiangchang_desktop_wake/wake.py b/src/jiangchang_desktop_wake/wake.py index 34cfa80..6c1fada 100644 --- a/src/jiangchang_desktop_wake/wake.py +++ b/src/jiangchang_desktop_wake/wake.py @@ -24,8 +24,9 @@ StrategyFn = Callable[[int, Profile], Tuple[str, str]] def _strategy_activate(hwnd: int, profile: Profile) -> Tuple[str, str]: - activate_hwnd(hwnd, maximize=True) - return "foreground+maximize", "ok" + maximize = bool(getattr(profile, "activate_maximize", True)) + activate_hwnd(hwnd, maximize=maximize) + return ("foreground+maximize" if maximize else "foreground"), "ok" def _strategy_wm_getobject(hwnd: int, _profile: Profile) -> Tuple[str, str]: diff --git a/tests/desktop_wake/test_profiles_and_types.py b/tests/desktop_wake/test_profiles_and_types.py index d332191..551497e 100644 --- a/tests/desktop_wake/test_profiles_and_types.py +++ b/tests/desktop_wake/test_profiles_and_types.py @@ -15,6 +15,7 @@ class ProfileTests(unittest.TestCase): ids = list_profiles() self.assertIn("wecom", ids) self.assertIn("generic", ids) + self.assertIn("wecom_search", ids) def test_load_wecom(self): p = load_profile("wecom") @@ -24,6 +25,17 @@ class ProfileTests(unittest.TestCase): self.assertGreaterEqual(p.warm_min_findall, 50) self.assertIn("native_helper", p.wake_strategies) self.assertIn("n2-wake", p.native_args or []) + self.assertTrue(p.activate_maximize) + + def test_load_wecom_search(self): + p = load_profile("wecom_search") + self.assertEqual(p.id, "wecom_search") + self.assertEqual(p.window_class, "SearchResultWindow2") + self.assertIn("search_result", p.warm_markers) + self.assertIn("user_item", p.warm_markers) + self.assertFalse(p.activate_maximize) + self.assertIn("native_helper", p.wake_strategies) + self.assertIn("n2-wake", p.native_args or []) def test_unknown_profile(self): from jiangchang_desktop_wake.errors import DesktopWakeError