ms-vite-plugin 1.4.41 → 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
@@ -52,361 +62,247 @@ 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)
69
+ ---
60
70
 
61
- 节点选择器是访问 UI 元素的核心工具,提供了灵活的筛选和查找机制。
62
-
63
- ### 创建节点选择器
64
-
65
- #### `createNodeSelector` - 创建节点选择器
66
-
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()` | 清空输入 |
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` 单位毫秒。
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}")
130
- ```
131
-
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
145
-
146
- # 名称模糊匹配
147
- def titleMatch(pattern: str) -> NodeSelector
148
-
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
147
+ nodes = selector.label("确定").getNodeInfo(5000)
148
+ one = selector.getOneNodeInfo(3000)
149
+ xml = selector.xml()
160
150
  ```
161
151
 
162
- **示例:**
163
-
164
- ```python
165
- # 查找标签为"确定"的按钮
166
- confirm_button = selector.label("确定").getOneNodeInfo(3000)
152
+ ### 筛选条件
167
153
 
168
- # 查找包含"登录"的元素
169
- login_elements = selector.labelMatch(".*登录.*").getNodeInfo(3000)
154
+ 可链式调用,多个条件同时满足。带 `Match` 的为模糊匹配。
170
155
 
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)
180
- ```
181
-
182
- ##### 类型与属性
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` |
183
161
 
184
162
  ```python
185
- # 精确匹配类型
186
- def type(type_title: str) -> NodeSelector
187
-
188
- # 类型模糊匹配
189
- def typeMatch(pattern: str) -> NodeSelector
190
-
191
- # 启用状态
192
- def enabled(flag: bool) -> NodeSelector
163
+ selector.label("确定").type("XCUIElementTypeButton").getOneNodeInfo(3000)
164
+ selector.labelMatch(".*登录.*").getNodeInfo(3000)
165
+ selector.type("XCUIElementTypeTextField").enabled(True).getOneNodeInfo(3000)
193
166
  ```
194
167
 
195
- **示例:**
168
+ 常见类型:`Button`、`StaticText`、`TextField`、`SecureTextField`、`TextView`、`SearchField`、`Cell`、`Table`、`CollectionView`、`PickerWheel`、`Switch`、`Slider` 等(完整名为 `XCUIElementType...`)。
196
169
 
197
- ```python
198
- from kuaijs import node
199
-
200
- selector = node.createNodeSelector()
170
+ ---
201
171
 
202
- # 查找所有按钮
203
- all_buttons = selector.type("XCUIElementTypeButton").getNodeInfo(3000)
172
+ ## 节点操作说明
204
173
 
205
- # 查找所有文本框
206
- text_fields = selector.typeMatch("XCUIElementTypeTextField").getNodeInfo(3000)
207
-
208
- # 查找启用的元素
209
- enabled_elements = selector.enabled(True).getNodeInfo(3000)
210
- ```
211
-
212
- ##### 位置与结构
174
+ ### 是否可点 / 滚到可见
213
175
 
214
176
  ```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
223
-
224
- # 边界范围
225
- def bounds(x: int, y: int, width: int, height: int) -> NodeSelector
226
-
227
- # XPath 路径
228
- def xpath(path: str) -> NodeSelector
177
+ if node.hittable():
178
+ node.tap()
179
+ else:
180
+ node.scrollToVisible()
181
+ node.tap()
229
182
  ```
230
183
 
231
- **说明:** `*_match` 支持正则/模糊匹配;`childCount` 支持表达式(如 `>0`)。
184
+ - `hittable()`:当前能否点到
185
+ - `scrollToVisible()`:手动滚到可见
186
+ - `tap` / `doubleTap` / `press`:不可见时会自动尝试滚到可见再操作
232
187
 
233
- **示例:**
188
+ ### 点击
234
189
 
235
190
  ```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)
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()
254
202
  ```
255
203
 
256
- ## 节点操作
204
+ ### 输入与设值
257
205
 
258
- `NodeSelector` 查找得到的 `NodeInfo` 支持直接调用操作方法(点击、无障碍手势等)。
206
+ | 方法 | 说明 |
207
+ | --- | --- |
208
+ | `clearInput()` | 清空 |
209
+ | `input(text)` | 追加输入 |
210
+ | `setValue(text)` | 按类型设置 |
259
211
 
260
- ### 点击操作
212
+ | 控件 | 传参 | 效果 |
213
+ | --- | --- | --- |
214
+ | `PickerWheel` | 选项文案,如 `"1小时"` | 滚到该选项 |
215
+ | 文本框类 | 字符串 | 追加输入,不先清空 |
261
216
 
262
217
  ```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
218
+ field.clearInput()
219
+ field.setValue("hello")
283
220
 
284
- # 清空输入
285
- def clearInput() -> bool
286
-
287
- # 追加输入
288
- def input(text: str) -> bool
289
-
290
- # 设置节点的值
291
- def setValue(text: str) -> bool
221
+ wheel = selector.type("XCUIElementTypePickerWheel").getOneNodeInfo(3000)
222
+ if wheel:
223
+ wheel.setValue("1小时")
292
224
  ```
293
225
 
294
- #### tap / doubleTap / press - 无障碍手势
295
-
296
- 通过无障碍 API 直接操作节点,不依赖坐标点击。
297
-
298
- | 方法 | 参数 | 说明 |
299
- | --- | --- | --- |
300
- | `tap()` | 无 | 无障碍单击 |
301
- | `doubleTap()` | 无 | 无障碍双击 |
302
- | `press(duration=500)` | `duration`:长按持续时间,单位毫秒,默认 500 | 无障碍长按 |
303
-
304
- **示例:**
226
+ ### 树导航
305
227
 
306
228
  ```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)
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()
321
235
  ```
322
236
 
323
- #### setValue - 设置节点的值
237
+ ---
324
238
 
325
- 按控件类型自动处理,常用支持如下:
239
+ ## 示例
326
240
 
327
- | 控件类型 | 传参示例 | 说明 |
328
- | --- | --- | --- |
329
- | `XCUIElementTypePickerWheel` | `"1小时"` | 滚轮选到对应选项文案 |
330
- | `XCUIElementTypeTextField` | `"hello"` | 先聚焦再输入文本 |
331
- | `XCUIElementTypeSecureTextField` | `"123456"` | 同上 |
332
- | `XCUIElementTypeTextView` | `"多行内容"` | 同上 |
333
- | `XCUIElementTypeSearchField` | `"关键词"` | 同上 |
334
-
335
- 文本类控件不会先清空已有内容;需要清空时先调用 `clearInput()`。
336
-
337
- **示例:**
241
+ ### 登录
338
242
 
339
243
  ```python
340
244
  from kuaijs import node
341
245
 
342
246
  selector = node.createNodeSelector()
343
247
 
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小时")
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()
367
270
  ```
368
271
 
369
- ### 节点导航
272
+ ### 列表里靠后的项
370
273
 
371
274
  ```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]
275
+ from kuaijs import node
386
276
 
387
- # 获取后面的兄弟节点
388
- def nextSiblings() -> List[NodeInfo]
277
+ selector = node.createNodeSelector({"mode": 2})
278
+ item = selector.label("第 30 项").getOneNodeInfo(5000)
279
+ if item:
280
+ item.tap() # 不在可见区域时会自动滚动
389
281
  ```
390
282
 
391
- **示例:**
283
+ ### 查看界面结构
392
284
 
393
285
  ```python
394
286
  from kuaijs import node
395
287
 
396
288
  selector = node.createNodeSelector()
289
+ xml = selector.xml(8000)
290
+ if xml:
291
+ print(xml)
292
+ ```
397
293
 
398
- # 查找输入框并获取其父容器
399
- text_field = selector.type("XCUIElementTypeTextField").getOneNodeInfo(3000)
294
+ ---
400
295
 
401
- if text_field:
402
- container = text_field.parent()
403
- if container:
404
- print(f"容器类型: {container.type}")
296
+ ## 常见问题
405
297
 
406
- # 获取容器的所有子元素
407
- children = container.allChildren()
408
- print(f"容器包含 {len(children)} 个子元素")
298
+ **找不到节点**
299
+ 加大超时;减少条件或改用 `*Match`;换 `mode`;用 `xml()` 看实际文案和类型。
409
300
 
410
- # 查找同级的按钮
411
- sibling_buttons = [n for n in children if n.type == "XCUIElementTypeButton"]
412
- ```
301
+ **点了没反应**
302
+ 先看 `hittable()`;确认点到的是目标控件;可对比 `clickCenter()`。
303
+
304
+ **输入叠在旧文字后面**
305
+ `input` / 文本类 `setValue` 是追加,先 `clearInput()`。
306
+
307
+ **滚轮设不中**
308
+ `setValue` 要传界面上显示的选项文案;确认类型是 `PickerWheel`;多列时选对那一列。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ms-vite-plugin",
3
- "version": "1.4.41",
3
+ "version": "1.4.43",
4
4
  "type": "commonjs",
5
5
  "license": "MIT",
6
6
  "publishConfig": {