ms-vite-plugin 1.4.40 → 1.4.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,15 +1,25 @@
1
1
  # 节点模块 (Node)
2
2
 
3
- 节点模块提供了强大的 UI 元素访问和操作功能,可以通过各种条件选择和操作界面元素。
3
+ 用于查找和操作当前界面上的控件节点。
4
4
 
5
- **注意事项**
5
+ **注意**
6
6
 
7
- - 此模块仅支持 快点 Agent 模式使用
7
+ - 仅支持快点 Agent 模式
8
+ - 通过 `from kuaijs import node` 使用
9
+
10
+ **基本流程**
11
+
12
+ ```text
13
+ createNodeSelector → 设置筛选条件 → getOneNodeInfo / getNodeInfo
14
+ → 点击 / 输入 / 设值等
15
+ ```
16
+
17
+ ---
8
18
 
9
19
  ## 类型定义
10
20
 
11
21
  ```python
12
- from typing import List, Literal, Optional, TypedDict, Union
22
+ from typing import List, Optional, TypedDict
13
23
 
14
24
  class NodeBounds:
15
25
  x: int
@@ -37,6 +47,13 @@ class NodeInfo:
37
47
  def clickCenter(self) -> bool: ...
38
48
  def clickRandom(self) -> bool: ...
39
49
  def hittable(self) -> bool: ...
50
+ def tap(self) -> bool: ...
51
+ def doubleTap(self) -> bool: ...
52
+ def press(self, duration: int = 500) -> bool: ...
53
+ def scrollToVisible(self) -> bool: ...
54
+ def clearInput(self) -> bool: ...
55
+ def input(self, text: str) -> bool: ...
56
+ def setValue(self, text: str) -> bool: ...
40
57
  def parent(self) -> Optional["NodeInfo"]: ...
41
58
  def child(self, index: int) -> Optional["NodeInfo"]: ...
42
59
  def allChildren(self) -> List["NodeInfo"]: ...
@@ -45,286 +62,247 @@ class NodeInfo:
45
62
  def nextSiblings(self) -> List["NodeInfo"]: ...
46
63
 
47
64
  class NodeSelectorOptions(TypedDict, total=False):
48
- maxDepth: int # 最大层级深度,默认 50
49
- mode: int # 抓取模式:模式 1、模式 2、模式 3,默认模式 1
65
+ maxDepth: int # 最大层级,默认 50
66
+ mode: int # 抓取模式 1 | 2 | 3,默认 1
50
67
  ```
51
68
 
52
- ## 节点选择器 (NodeSelector)
53
-
54
- 节点选择器是访问 UI 元素的核心工具,提供了灵活的筛选和查找机制。
69
+ ---
55
70
 
56
- ### 创建节点选择器
57
-
58
- #### `createNodeSelector` - 创建节点选择器
59
-
60
- 创建一个新的节点选择器实例。
71
+ ## 创建选择器
61
72
 
62
73
  ```python
63
74
  def createNodeSelector(params: Optional[NodeSelectorOptions] = None) -> NodeSelector
64
75
  ```
65
76
 
66
- **参数说明:**
67
-
68
- | 参数名 | 类型 | 是否必填 | 默认值 | 描述 |
69
- | ---------- | ---- | -------- | ------ | --------------------- |
70
- | `maxDepth` | int | 否 | 50 | 最大层级深度,默认 50 |
71
- | `mode` | int | 否 | 1 | 抓取模式。支持模式 1、模式 2、模式 3,默认模式 1 |
72
-
73
- **返回值:** `NodeSelector`
74
-
75
- **示例:**
77
+ | 参数 | 默认 | 说明 |
78
+ | --- | --- | --- |
79
+ | `maxDepth` | `50` | 抓取深度,深层控件抓不到时可调大 |
80
+ | `mode` | `1` | 抓取模式,可选 `1` / `2` / `3`,默认 `1` |
76
81
 
77
82
  ```python
78
83
  from kuaijs import node
79
84
 
80
- # 创建选择器
81
- selector = node.createNodeSelector({
82
- "maxDepth": 20, # 最大20层深度
83
- "mode": 1 # 默认模式 1
84
- })
85
- ```
86
-
87
- ### 选择器方法
88
-
89
- #### 获取节点
90
-
91
- ```python
92
- # 获取当前页面的 XML 字符串
93
- def xml(timeout: int = 5000) -> Optional[str]
94
-
95
- # 获取多个节点
96
- def getNodeInfo(timeout: int = 5000) -> List[NodeInfo]
97
-
98
- # 获取单个节点
99
- def getOneNodeInfo(timeout: int = 5000) -> Optional[NodeInfo]
100
-
101
- # 加载节点数据(通常内部自动调用)
102
- def loadNode(timeout: int = 5000) -> NodeSelector
103
-
104
- # 清除所有选择条件
105
- def clearSelector() -> NodeSelector
85
+ selector = node.createNodeSelector()
86
+ shallow = node.createNodeSelector({"maxDepth": 15})
87
+ list_page = node.createNodeSelector({"mode": 2})
106
88
  ```
107
89
 
108
- **示例:**
90
+ ---
91
+
92
+ ## 节点信息
93
+
94
+ ### 属性
95
+
96
+ | 字段 | 说明 |
97
+ | --- | --- |
98
+ | `id` | 节点 ID(内部定位用,不要改) |
99
+ | `identifier` | 控件标识符 |
100
+ | `label` | 标签文案 |
101
+ | `type` | 类型,如 `XCUIElementTypeButton` |
102
+ | `value` | 当前值 |
103
+ | `placeholderValue` | 输入框占位文字 |
104
+ | `title` | 标题 |
105
+ | `enabled` | 是否启用 |
106
+ | `bounds` | 位置大小 |
107
+ | `depth` / `index` | 层级 / 序号 |
108
+ | `parentId` / `childCount` | 父节点 ID / 子节点数 |
109
+
110
+ ### 方法
111
+
112
+ | 方法 | 说明 |
113
+ | --- | --- |
114
+ | `hittable()` | 当前是否可点击 |
115
+ | `scrollToVisible()` | 滚动到可见区域 |
116
+ | `tap()` | 单击(不可见时会先尝试滚到可见) |
117
+ | `doubleTap()` | 双击 |
118
+ | `press(duration=500)` | 长按,毫秒,默认 500 |
119
+ | `clickCenter()` | 点击中心 |
120
+ | `clickRandom()` | 范围内随机点击 |
121
+ | `clearInput()` | 清空输入 |
122
+ | `input(text)` | 追加输入(不先清空) |
123
+ | `setValue(text)` | 设置值(见下方) |
124
+ | `parent()` / `child(i)` / `allChildren()` | 父 / 子 / 全部子节点 |
125
+ | `siblings()` / `previousSiblings()` / `nextSiblings()` | 兄弟节点 |
126
+
127
+ ---
128
+
129
+ ## 选择器方法
130
+
131
+ ### 加载与结果
132
+
133
+ | 方法 | 说明 |
134
+ | --- | --- |
135
+ | `loadNode(timeout=5000)` | 重新加载界面节点 |
136
+ | `clearSelector()` | 清除筛选条件 |
137
+ | `xml(timeout=5000)` | 获取界面 XML,失败返回 `None` |
138
+ | `getNodeInfo(timeout=5000)` | 所有匹配节点 |
139
+ | `getOneNodeInfo(timeout=5000)` | 第一个匹配节点,没有则 `None` |
140
+
141
+ `timeout` 单位毫秒。
109
142
 
110
143
  ```python
111
144
  from kuaijs import node
112
145
 
113
146
  selector = node.createNodeSelector()
114
-
115
- # 获取所有匹配的节点
116
- all_nodes = selector.getNodeInfo(5000)
117
- print(f"找到 {len(all_nodes)} 个节点")
118
-
119
- # 获取第一个匹配的节点
120
- first_node = selector.getOneNodeInfo(3000)
121
- if first_node:
122
- print(f"节点标签: {first_node.label}")
147
+ nodes = selector.label("确定").getNodeInfo(5000)
148
+ one = selector.getOneNodeInfo(3000)
149
+ xml = selector.xml()
123
150
  ```
124
151
 
125
- #### 条件筛选
126
-
127
- ##### 文本相关
128
-
129
- ```python
130
- # 精确匹配标签
131
- def label(text: str) -> NodeSelector
132
-
133
- # 标签模糊匹配
134
- def labelMatch(pattern: str) -> NodeSelector
135
-
136
- # 精确匹配名称
137
- def title(text: str) -> NodeSelector
152
+ ### 筛选条件
138
153
 
139
- # 名称模糊匹配
140
- def titleMatch(pattern: str) -> NodeSelector
154
+ 可链式调用,多个条件同时满足。带 `Match` 的为模糊匹配。
141
155
 
142
- # 精确匹配标识符
143
- def identifier(text: str) -> NodeSelector
156
+ | 分类 | 方法 |
157
+ | --- | --- |
158
+ | 文本 | `label` / `labelMatch`、`title` / `titleMatch`、`identifier` / `identifierMatch`、`value` / `valueMatch`、`placeholderValue` / `placeholderValueMatch` |
159
+ | 类型状态 | `type` / `typeMatch`、`enabled` |
160
+ | 结构位置 | `index`、`depth`、`childCount`(支持 `">0"`)、`bounds`、`xpath`、`id` |
144
161
 
145
- # 标识符模糊匹配
146
- def identifierMatch(pattern: str) -> NodeSelector
147
-
148
- # 精确匹配值
149
- def value(text: str) -> NodeSelector
150
-
151
- # 值模糊匹配
152
- def valueMatch(pattern: str) -> NodeSelector
162
+ ```python
163
+ selector.label("确定").type("XCUIElementTypeButton").getOneNodeInfo(3000)
164
+ selector.labelMatch(".*登录.*").getNodeInfo(3000)
165
+ selector.type("XCUIElementTypeTextField").enabled(True).getOneNodeInfo(3000)
153
166
  ```
154
167
 
155
- **示例:**
156
-
157
- ```python
158
- # 查找标签为"确定"的按钮
159
- confirm_button = selector.label("确定").getOneNodeInfo(3000)
168
+ 常见类型:`Button`、`StaticText`、`TextField`、`SecureTextField`、`TextView`、`SearchField`、`Cell`、`Table`、`CollectionView`、`PickerWheel`、`Switch`、`Slider` 等(完整名为 `XCUIElementType...`)。
160
169
 
161
- # 查找包含"登录"的元素
162
- login_elements = selector.labelMatch(".*登录.*").getNodeInfo(3000)
170
+ ---
163
171
 
164
- # 查找名称包含"button"的元素
165
- buttons = selector.titleMatch("button").getNodeInfo(3000)
172
+ ## 节点操作说明
166
173
 
167
- # 查找值为"submit"的元素
168
- submit_element = selector.value("submit").getOneNodeInfo(2000)
174
+ ### 是否可点 / 滚到可见
169
175
 
170
- # 通过标识符查找
171
- el_by_id = selector.identifier("login_btn").getOneNodeInfo(2000)
172
- fuzzy = selector.identifierMatch("^login_.*").getNodeInfo(3000)
176
+ ```python
177
+ if node.hittable():
178
+ node.tap()
179
+ else:
180
+ node.scrollToVisible()
181
+ node.tap()
173
182
  ```
174
183
 
175
- ##### 类型与属性
176
-
177
- ```python
178
- # 精确匹配类型
179
- def type(type_title: str) -> NodeSelector
184
+ - `hittable()`:当前能否点到
185
+ - `scrollToVisible()`:手动滚到可见
186
+ - `tap` / `doubleTap` / `press`:不可见时会自动尝试滚到可见再操作
180
187
 
181
- # 类型模糊匹配
182
- def typeMatch(pattern: str) -> NodeSelector
188
+ ### 点击
183
189
 
184
- # 启用状态
185
- def enabled(flag: bool) -> NodeSelector
190
+ ```python
191
+ btn = (
192
+ selector.label("确定")
193
+ .type("XCUIElementTypeButton")
194
+ .getOneNodeInfo(3000)
195
+ )
196
+ if btn:
197
+ btn.tap()
198
+ btn.doubleTap()
199
+ btn.press(800)
200
+ # btn.clickCenter()
201
+ # btn.clickRandom()
186
202
  ```
187
203
 
188
- **示例:**
204
+ ### 输入与设值
189
205
 
190
- ```python
191
- from kuaijs import node
206
+ | 方法 | 说明 |
207
+ | --- | --- |
208
+ | `clearInput()` | 清空 |
209
+ | `input(text)` | 追加输入 |
210
+ | `setValue(text)` | 按类型设置 |
192
211
 
193
- selector = node.createNodeSelector()
212
+ | 控件 | 传参 | 效果 |
213
+ | --- | --- | --- |
214
+ | `PickerWheel` | 选项文案,如 `"1小时"` | 滚到该选项 |
215
+ | 文本框类 | 字符串 | 追加输入,不先清空 |
194
216
 
195
- # 查找所有按钮
196
- all_buttons = selector.type("XCUIElementTypeButton").getNodeInfo(3000)
197
-
198
- # 查找所有文本框
199
- text_fields = selector.typeMatch("XCUIElementTypeTextField").getNodeInfo(3000)
217
+ ```python
218
+ field.clearInput()
219
+ field.setValue("hello")
200
220
 
201
- # 查找启用的元素
202
- enabled_elements = selector.enabled(True).getNodeInfo(3000)
221
+ wheel = selector.type("XCUIElementTypePickerWheel").getOneNodeInfo(3000)
222
+ if wheel:
223
+ wheel.setValue("1小时")
203
224
  ```
204
225
 
205
- ##### 位置与结构
226
+ ### 树导航
206
227
 
207
228
  ```python
208
- # 索引位置
209
- def index(idx: int) -> NodeSelector
210
-
211
- # 层级深度
212
- def depth(d: int) -> NodeSelector
213
-
214
- # 子节点数量(支持表达式,如 ">0")
215
- def childCount(expr: Union[int, str]) -> NodeSelector
216
-
217
- # 边界范围
218
- def bounds(x: int, y: int, width: int, height: int) -> NodeSelector
219
-
220
- # XPath 路径
221
- def xpath(path: str) -> NodeSelector
229
+ field = selector.type("XCUIElementTypeTextField").getOneNodeInfo(3000)
230
+ if field:
231
+ parent = field.parent()
232
+ children = parent.allChildren() if parent else []
233
+ prev = field.previousSiblings()
234
+ next_ = field.nextSiblings()
222
235
  ```
223
236
 
224
- **说明:** `*_match` 支持正则/模糊匹配;`childCount` 支持表达式(如 `>0`)。
237
+ ---
225
238
 
226
- **示例:**
239
+ ## 示例
240
+
241
+ ### 登录
227
242
 
228
243
  ```python
229
244
  from kuaijs import node
230
245
 
231
246
  selector = node.createNodeSelector()
232
247
 
233
- # 查找第一个元素
234
- first_element = selector.index(0).getOneNodeInfo(2000)
235
-
236
- # 查找特定深度的元素
237
- level3_elements = selector.depth(3).getNodeInfo(3000)
238
-
239
- # 查找有子节点的元素
240
- parent_elements = selector.childCount(">0").getNodeInfo(3000)
241
-
242
- # 查找特定区域的元素
243
- region_elements = selector.bounds(100, 100, 200, 50).getNodeInfo(3000)
244
-
245
- # 使用 XPath 查找
246
- xpath_elements = selector.xpath("//*[@title='快点JS']").getNodeInfo(3000)
248
+ user = selector.type("XCUIElementTypeTextField").getOneNodeInfo(5000)
249
+ if user:
250
+ user.clearInput()
251
+ user.setValue("demo_user")
252
+
253
+ password = (
254
+ selector.clearSelector()
255
+ .type("XCUIElementTypeSecureTextField")
256
+ .getOneNodeInfo(3000)
257
+ )
258
+ if password:
259
+ password.clearInput()
260
+ password.setValue("password")
261
+
262
+ login = (
263
+ selector.clearSelector()
264
+ .labelMatch(".*登录.*")
265
+ .type("XCUIElementTypeButton")
266
+ .getOneNodeInfo(3000)
267
+ )
268
+ if login:
269
+ login.tap()
247
270
  ```
248
271
 
249
- ## 节点操作
250
-
251
- `NodeSelector` 查找得到的 `NodeInfo` 支持直接调用操作方法(点击、无障碍手势等)。
252
-
253
- ### 点击操作
272
+ ### 列表里靠后的项
254
273
 
255
274
  ```python
256
- # 点击节点中心
257
- def clickCenter() -> bool
258
-
259
- # 随机点击节点区域
260
- def clickRandom() -> bool
275
+ from kuaijs import node
261
276
 
262
- # 节点是否可接收事件(用于判断是否显示在屏幕上)
263
- def hittable() -> bool
277
+ selector = node.createNodeSelector({"mode": 2})
278
+ item = selector.label("第 30 项").getOneNodeInfo(5000)
279
+ if item:
280
+ item.tap() # 不在可见区域时会自动滚动
264
281
  ```
265
282
 
266
- **示例:**
283
+ ### 查看界面结构
267
284
 
268
285
  ```python
269
286
  from kuaijs import node
270
287
 
271
288
  selector = node.createNodeSelector()
272
-
273
- # 查找并点击确定按钮
274
- confirm_btn = selector.label("确定").type("XCUIElementTypeButton").getOneNodeInfo(3000)
275
-
276
- if confirm_btn:
277
- print(f"是否可接收事件: {confirm_btn.hittable()}")
278
-
279
- clicked = confirm_btn.clickCenter()
280
- if clicked:
281
- print("成功点击确定按钮")
282
-
283
- # 随机点击避免检测
284
- confirm_btn.clickRandom()
285
- ```
286
-
287
- ### 节点导航
288
-
289
- ```python
290
- # 获取父节点
291
- def parent() -> Optional[NodeInfo]
292
-
293
- # 获取指定索引的子节点
294
- def child(index: int) -> Optional[NodeInfo]
295
-
296
- # 获取所有子节点
297
- def allChildren() -> List[NodeInfo]
298
-
299
- # 获取兄弟节点
300
- def siblings() -> List[NodeInfo]
301
-
302
- # 获取前面的兄弟节点
303
- def previousSiblings() -> List[NodeInfo]
304
-
305
- # 获取后面的兄弟节点
306
- def nextSiblings() -> List[NodeInfo]
289
+ xml = selector.xml(8000)
290
+ if xml:
291
+ print(xml)
307
292
  ```
308
293
 
309
- **示例:**
294
+ ---
310
295
 
311
- ```python
312
- from kuaijs import node
313
-
314
- selector = node.createNodeSelector()
296
+ ## 常见问题
315
297
 
316
- # 查找输入框并获取其父容器
317
- text_field = selector.type("XCUIElementTypeTextField").getOneNodeInfo(3000)
298
+ **找不到节点**
299
+ 加大超时;减少条件或改用 `*Match`;换 `mode`;用 `xml()` 看实际文案和类型。
318
300
 
319
- if text_field:
320
- container = text_field.parent()
321
- if container:
322
- print(f"容器类型: {container.type}")
301
+ **点了没反应**
302
+ 先看 `hittable()`;确认点到的是目标控件;可对比 `clickCenter()`。
323
303
 
324
- # 获取容器的所有子元素
325
- children = container.allChildren()
326
- print(f"容器包含 {len(children)} 个子元素")
304
+ **输入叠在旧文字后面**
305
+ `input` / 文本类 `setValue` 是追加,先 `clearInput()`。
327
306
 
328
- # 查找同级的按钮
329
- sibling_buttons = [n for n in children if n.type == "XCUIElementTypeButton"]
330
- ```
307
+ **滚轮设不中**
308
+ `setValue` 要传界面上显示的选项文案;确认类型是 `PickerWheel`;多列时选对那一列。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ms-vite-plugin",
3
- "version": "1.4.40",
3
+ "version": "1.4.43",
4
4
  "type": "commonjs",
5
5
  "license": "MIT",
6
6
  "publishConfig": {