ms-vite-plugin 1.4.41 → 1.4.45

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
@@ -52,361 +62,250 @@ class NodeInfo:
52
62
  def nextSiblings(self) -> List["NodeInfo"]: ...
53
63
 
54
64
  class NodeSelectorOptions(TypedDict, total=False):
55
- maxDepth: int # 最大层级深度,默认 50
56
- mode: int # 抓取模式:模式 1、模式 2、模式 3,默认模式 1
65
+ maxDepth: int # 最大层级,默认 50
66
+ mode: int # 抓取模式 1 | 2 | 3,默认 1
57
67
  ```
58
68
 
59
- ## 节点选择器 (NodeSelector)
60
-
61
- 节点选择器是访问 UI 元素的核心工具,提供了灵活的筛选和查找机制。
62
-
63
- ### 创建节点选择器
64
-
65
- #### `createNodeSelector` - 创建节点选择器
69
+ ---
66
70
 
67
- 创建一个新的节点选择器实例。
71
+ ## 创建选择器
68
72
 
69
73
  ```python
70
74
  def createNodeSelector(params: Optional[NodeSelectorOptions] = None) -> NodeSelector
71
75
  ```
72
76
 
73
- **参数说明:**
74
-
75
- | 参数名 | 类型 | 是否必填 | 默认值 | 描述 |
76
- | ---------- | ---- | -------- | ------ | --------------------- |
77
- | `maxDepth` | int | 否 | 50 | 最大层级深度,默认 50 |
78
- | `mode` | int | 否 | 1 | 抓取模式。支持模式 1、模式 2、模式 3,默认模式 1 |
79
-
80
- **返回值:** `NodeSelector`
81
-
82
- **示例:**
77
+ | 参数 | 默认 | 说明 |
78
+ | --- | --- | --- |
79
+ | `maxDepth` | `50` | 抓取深度,深层控件抓不到时可调大 |
80
+ | `mode` | `1` | 抓取模式,可选 `1` / `2` / `3`,默认 `1` |
83
81
 
84
82
  ```python
85
83
  from kuaijs import node
86
84
 
87
- # 创建选择器
88
- selector = node.createNodeSelector({
89
- "maxDepth": 20, # 最大20层深度
90
- "mode": 1 # 默认模式 1
91
- })
92
- ```
93
-
94
- ### 选择器方法
95
-
96
- #### 获取节点
97
-
98
- ```python
99
- # 获取当前页面的 XML 字符串
100
- def xml(timeout: int = 5000) -> Optional[str]
101
-
102
- # 获取多个节点
103
- def getNodeInfo(timeout: int = 5000) -> List[NodeInfo]
104
-
105
- # 获取单个节点
106
- def getOneNodeInfo(timeout: int = 5000) -> Optional[NodeInfo]
107
-
108
- # 加载节点数据(通常内部自动调用)
109
- def loadNode(timeout: int = 5000) -> NodeSelector
110
-
111
- # 清除所有选择条件
112
- def clearSelector() -> NodeSelector
85
+ selector = node.createNodeSelector()
86
+ shallow = node.createNodeSelector({"maxDepth": 15})
87
+ list_page = node.createNodeSelector({"mode": 2})
113
88
  ```
114
89
 
115
- **示例:**
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()` | 清空输入(内部会先 `tap` 聚焦,因而也会自动滚到可见) |
122
+ | `input(text)` | 追加输入(不先清空;内部会先 `tap` 聚焦) |
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` 单位毫秒。
116
142
 
117
143
  ```python
118
144
  from kuaijs import node
119
145
 
120
146
  selector = node.createNodeSelector()
121
-
122
- # 获取所有匹配的节点
123
- all_nodes = selector.getNodeInfo(5000)
124
- print(f"找到 {len(all_nodes)} 个节点")
125
-
126
- # 获取第一个匹配的节点
127
- first_node = selector.getOneNodeInfo(3000)
128
- if first_node:
129
- print(f"节点标签: {first_node.label}")
147
+ nodes = selector.label("确定").getNodeInfo(5000)
148
+ one = selector.getOneNodeInfo(3000)
149
+ xml = selector.xml()
130
150
  ```
131
151
 
132
- #### 条件筛选
133
-
134
- ##### 文本相关
135
-
136
- ```python
137
- # 精确匹配标签
138
- def label(text: str) -> NodeSelector
139
-
140
- # 标签模糊匹配
141
- def labelMatch(pattern: str) -> NodeSelector
142
-
143
- # 精确匹配名称
144
- def title(text: str) -> NodeSelector
152
+ ### 筛选条件
145
153
 
146
- # 名称模糊匹配
147
- def titleMatch(pattern: str) -> NodeSelector
154
+ 可链式调用,多个条件同时满足。带 `Match` 的为模糊匹配。
148
155
 
149
- # 精确匹配标识符
150
- def identifier(text: str) -> NodeSelector
151
-
152
- # 标识符模糊匹配
153
- def identifierMatch(pattern: str) -> NodeSelector
154
-
155
- # 精确匹配值
156
- def value(text: str) -> NodeSelector
157
-
158
- # 值模糊匹配
159
- def valueMatch(pattern: str) -> NodeSelector
160
- ```
161
-
162
- **示例:**
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` |
163
161
 
164
162
  ```python
165
- # 查找标签为"确定"的按钮
166
- confirm_button = selector.label("确定").getOneNodeInfo(3000)
167
-
168
- # 查找包含"登录"的元素
169
- login_elements = selector.labelMatch(".*登录.*").getNodeInfo(3000)
170
-
171
- # 查找名称包含"button"的元素
172
- buttons = selector.titleMatch("button").getNodeInfo(3000)
173
-
174
- # 查找值为"submit"的元素
175
- submit_element = selector.value("submit").getOneNodeInfo(2000)
176
-
177
- # 通过标识符查找
178
- el_by_id = selector.identifier("login_btn").getOneNodeInfo(2000)
179
- fuzzy = selector.identifierMatch("^login_.*").getNodeInfo(3000)
163
+ selector.label("确定").type("XCUIElementTypeButton").getOneNodeInfo(3000)
164
+ selector.labelMatch(".*登录.*").getNodeInfo(3000)
165
+ selector.type("XCUIElementTypeTextField").enabled(True).getOneNodeInfo(3000)
180
166
  ```
181
167
 
182
- ##### 类型与属性
168
+ 常见类型:`Button`、`StaticText`、`TextField`、`SecureTextField`、`TextView`、`SearchField`、`Cell`、`Table`、`CollectionView`、`PickerWheel`、`Switch`、`Slider` 等(完整名为 `XCUIElementType...`)。
183
169
 
184
- ```python
185
- # 精确匹配类型
186
- def type(type_title: str) -> NodeSelector
170
+ ---
187
171
 
188
- # 类型模糊匹配
189
- def typeMatch(pattern: str) -> NodeSelector
172
+ ## 节点操作说明
190
173
 
191
- # 启用状态
192
- def enabled(flag: bool) -> NodeSelector
193
- ```
174
+ ### 是否可点 / 滚到可见
194
175
 
195
- **示例:**
176
+ `tap` / `doubleTap` / `press` / `setValue`(以及内部会先 `tap` 的 `clearInput` / `input`)在元素当前不可见时,会**自动**先滚到可见再操作,一般不用手写滚动:
196
177
 
197
178
  ```python
198
- from kuaijs import node
199
-
200
- selector = node.createNodeSelector()
201
-
202
- # 查找所有按钮
203
- all_buttons = selector.type("XCUIElementTypeButton").getNodeInfo(3000)
204
-
205
- # 查找所有文本框
206
- text_fields = selector.typeMatch("XCUIElementTypeTextField").getNodeInfo(3000)
207
-
208
- # 查找启用的元素
209
- enabled_elements = selector.enabled(True).getNodeInfo(3000)
179
+ # 推荐:直接操作,不可见时会自动滚到可见
180
+ node.tap()
181
+ node.doubleTap()
182
+ node.press(800)
183
+ node.setValue("hello")
210
184
  ```
211
185
 
212
- ##### 位置与结构
213
-
214
- ```python
215
- # 索引位置
216
- def index(idx: int) -> NodeSelector
217
-
218
- # 层级深度
219
- def depth(d: int) -> NodeSelector
220
-
221
- # 子节点数量(支持表达式,如 ">0")
222
- def childCount(expr: Union[int, str]) -> NodeSelector
186
+ - `hittable()`:当前能否点到(可见且未被挡住),用于排查“点了没反应”
187
+ - `scrollToVisible()`:只滚到可见、不点击;仅在你想先露出元素、稍后再操作时使用
188
+ - `clickCenter()` / `clickRandom()`:按坐标点击,**不会**自动滚动
223
189
 
224
- # 边界范围
225
- def bounds(x: int, y: int, width: int, height: int) -> NodeSelector
226
-
227
- # XPath 路径
228
- def xpath(path: str) -> NodeSelector
229
- ```
230
-
231
- **说明:** `*_match` 支持正则/模糊匹配;`childCount` 支持表达式(如 `>0`)。
232
-
233
- **示例:**
190
+ ### 点击
234
191
 
235
192
  ```python
236
- from kuaijs import node
237
-
238
- selector = node.createNodeSelector()
239
-
240
- # 查找第一个元素
241
- first_element = selector.index(0).getOneNodeInfo(2000)
242
-
243
- # 查找特定深度的元素
244
- level3_elements = selector.depth(3).getNodeInfo(3000)
245
-
246
- # 查找有子节点的元素
247
- parent_elements = selector.childCount(">0").getNodeInfo(3000)
248
-
249
- # 查找特定区域的元素
250
- region_elements = selector.bounds(100, 100, 200, 50).getNodeInfo(3000)
251
-
252
- # 使用 XPath 查找
253
- xpath_elements = selector.xpath("//*[@title='快点JS']").getNodeInfo(3000)
193
+ btn = (
194
+ selector.label("确定")
195
+ .type("XCUIElementTypeButton")
196
+ .getOneNodeInfo(3000)
197
+ )
198
+ if btn:
199
+ btn.tap() # 不可见时自动滚到可见
200
+ btn.doubleTap() # 同上
201
+ btn.press(800) # 同上
202
+ # btn.clickCenter() # 坐标点击,不自动滚动
203
+ # btn.clickRandom()
254
204
  ```
255
205
 
256
- ## 节点操作
206
+ ### 输入与设值
257
207
 
258
- `NodeSelector` 查找得到的 `NodeInfo` 支持直接调用操作方法(点击、无障碍手势等)。
208
+ | 方法 | 说明 |
209
+ | --- | --- |
210
+ | `clearInput()` | 清空(内部先 `tap` 聚焦,不可见时也会自动滚到可见) |
211
+ | `input(text)` | 追加输入(内部先 `tap` 聚焦) |
212
+ | `setValue(text)` | 按类型设置;不可见时会先自动滚到可见 |
259
213
 
260
- ### 点击操作
214
+ | 控件 | 传参 | 效果 |
215
+ | --- | --- | --- |
216
+ | `PickerWheel` | 选项文案,如 `"1小时"` | 滚到该选项(控件本身不可见时也会先自动滚到可见) |
217
+ | 文本框类 | 字符串 | 追加输入,不先清空 |
261
218
 
262
219
  ```python
263
- # 点击节点中心(坐标点击)
264
- def clickCenter() -> bool
265
-
266
- # 随机点击节点区域(坐标点击)
267
- def clickRandom() -> bool
268
-
269
- # 节点是否可接收事件(用于判断是否显示在屏幕上)
270
- def hittable() -> bool
271
-
272
- # 无障碍点击
273
- def tap() -> bool
274
-
275
- # 无障碍双击
276
- def doubleTap() -> bool
277
-
278
- # 无障碍长按,duration 单位毫秒,默认 500
279
- def press(duration: int = 500) -> bool
280
-
281
- # 将节点滚动到可见区域
282
- def scrollToVisible() -> bool
220
+ field.clearInput()
221
+ field.setValue("hello")
283
222
 
284
- # 清空输入
285
- def clearInput() -> bool
286
-
287
- # 追加输入
288
- def input(text: str) -> bool
289
-
290
- # 设置节点的值
291
- def setValue(text: str) -> bool
223
+ wheel = selector.type("XCUIElementTypePickerWheel").getOneNodeInfo(3000)
224
+ if wheel:
225
+ # 不可见时会自动滚到可见,再调到目标选项
226
+ wheel.setValue("1小时")
292
227
  ```
293
228
 
294
- #### tap / doubleTap / press - 无障碍手势
295
-
296
- 通过无障碍 API 直接操作节点,不依赖坐标点击。
297
-
298
- | 方法 | 参数 | 说明 |
299
- | --- | --- | --- |
300
- | `tap()` | 无 | 无障碍单击 |
301
- | `doubleTap()` | 无 | 无障碍双击 |
302
- | `press(duration=500)` | `duration`:长按持续时间,单位毫秒,默认 500 | 无障碍长按 |
303
-
304
- **示例:**
229
+ ### 树导航
305
230
 
306
231
  ```python
307
- from kuaijs import node
308
-
309
- selector = node.createNodeSelector()
310
-
311
- btn = selector.label("确定").type("XCUIElementTypeButton").getOneNodeInfo(3000)
312
- if btn:
313
- # 无障碍单击
314
- btn.tap()
315
-
316
- # 无障碍双击
317
- btn.doubleTap()
318
-
319
- # 无障碍长按 800ms
320
- btn.press(800)
232
+ field = selector.type("XCUIElementTypeTextField").getOneNodeInfo(3000)
233
+ if field:
234
+ parent = field.parent()
235
+ children = parent.allChildren() if parent else []
236
+ prev = field.previousSiblings()
237
+ next_ = field.nextSiblings()
321
238
  ```
322
239
 
323
- #### setValue - 设置节点的值
324
-
325
- 按控件类型自动处理,常用支持如下:
326
-
327
- | 控件类型 | 传参示例 | 说明 |
328
- | --- | --- | --- |
329
- | `XCUIElementTypePickerWheel` | `"1小时"` | 滚轮选到对应选项文案 |
330
- | `XCUIElementTypeTextField` | `"hello"` | 先聚焦再输入文本 |
331
- | `XCUIElementTypeSecureTextField` | `"123456"` | 同上 |
332
- | `XCUIElementTypeTextView` | `"多行内容"` | 同上 |
333
- | `XCUIElementTypeSearchField` | `"关键词"` | 同上 |
240
+ ---
334
241
 
335
- 文本类控件不会先清空已有内容;需要清空时先调用 `clearInput()`。
242
+ ## 示例
336
243
 
337
- **示例:**
244
+ ### 登录
338
245
 
339
246
  ```python
340
247
  from kuaijs import node
341
248
 
342
249
  selector = node.createNodeSelector()
343
250
 
344
- # 查找并点击确定按钮
345
- confirm_btn = selector.label("确定").type("XCUIElementTypeButton").getOneNodeInfo(3000)
346
-
347
- if confirm_btn:
348
- print(f"是否可接收事件: {confirm_btn.hittable()}")
349
-
350
- confirm_btn.scrollToVisible()
351
- # 优先使用无障碍点击;也可用 clickCenter / clickRandom 做坐标点击
352
- clicked = confirm_btn.tap()
353
- if clicked:
354
- print("成功点击确定按钮")
355
-
356
- # 随机坐标点击避免检测
357
- confirm_btn.clickRandom()
358
-
359
- field = selector.type("XCUIElementTypeTextField").getOneNodeInfo(3000)
360
- if field:
361
- field.clearInput()
362
- field.setValue("hello")
363
-
364
- wheel = selector.type("XCUIElementTypePickerWheel").getOneNodeInfo(3000)
365
- if wheel:
366
- wheel.setValue("1小时")
251
+ user = selector.type("XCUIElementTypeTextField").getOneNodeInfo(5000)
252
+ if user:
253
+ user.clearInput()
254
+ user.setValue("demo_user")
255
+
256
+ password = (
257
+ selector.clearSelector()
258
+ .type("XCUIElementTypeSecureTextField")
259
+ .getOneNodeInfo(3000)
260
+ )
261
+ if password:
262
+ password.clearInput()
263
+ password.setValue("password")
264
+
265
+ login = (
266
+ selector.clearSelector()
267
+ .labelMatch(".*登录.*")
268
+ .type("XCUIElementTypeButton")
269
+ .getOneNodeInfo(3000)
270
+ )
271
+ if login:
272
+ login.tap()
367
273
  ```
368
274
 
369
- ### 节点导航
275
+ ### 列表里靠后的项
370
276
 
371
277
  ```python
372
- # 获取父节点
373
- def parent() -> Optional[NodeInfo]
374
-
375
- # 获取指定索引的子节点
376
- def child(index: int) -> Optional[NodeInfo]
377
-
378
- # 获取所有子节点
379
- def allChildren() -> List[NodeInfo]
380
-
381
- # 获取兄弟节点
382
- def siblings() -> List[NodeInfo]
383
-
384
- # 获取前面的兄弟节点
385
- def previousSiblings() -> List[NodeInfo]
278
+ from kuaijs import node
386
279
 
387
- # 获取后面的兄弟节点
388
- def nextSiblings() -> List[NodeInfo]
280
+ selector = node.createNodeSelector({"mode": 2})
281
+ item = selector.label("第 30 项").getOneNodeInfo(5000)
282
+ if item:
283
+ item.tap() # 不在可见区域时会自动滚动
389
284
  ```
390
285
 
391
- **示例:**
286
+ ### 查看界面结构
392
287
 
393
288
  ```python
394
289
  from kuaijs import node
395
290
 
396
291
  selector = node.createNodeSelector()
292
+ xml = selector.xml(8000)
293
+ if xml:
294
+ print(xml)
295
+ ```
397
296
 
398
- # 查找输入框并获取其父容器
399
- text_field = selector.type("XCUIElementTypeTextField").getOneNodeInfo(3000)
297
+ ---
400
298
 
401
- if text_field:
402
- container = text_field.parent()
403
- if container:
404
- print(f"容器类型: {container.type}")
299
+ ## 常见问题
405
300
 
406
- # 获取容器的所有子元素
407
- children = container.allChildren()
408
- print(f"容器包含 {len(children)} 个子元素")
301
+ **找不到节点**
302
+ 加大超时;减少条件或改用 `*Match`;换 `mode`;用 `xml()` 看实际文案和类型。
409
303
 
410
- # 查找同级的按钮
411
- sibling_buttons = [n for n in children if n.type == "XCUIElementTypeButton"]
412
- ```
304
+ **点了没反应**
305
+ 先看 `hittable()`;确认点到的是目标控件;可对比 `clickCenter()`。
306
+
307
+ **输入叠在旧文字后面**
308
+ `input` / 文本类 `setValue` 是追加,先 `clearInput()`。
309
+
310
+ **滚轮设不中**
311
+ `setValue` 要传界面上显示的选项文案;确认类型是 `PickerWheel`;多列时选对那一列。